@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,20 @@
1
+ window.SCENE_LIBS=['lib/three.min.js'];window.PUNCH=0;window.ERAS=[
2
+ {id:"38_giotto",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("乔托",50,1034);c.restore();}}},
3
+ {id:"39_piero",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("皮耶罗",50,1034);c.restore();}}},
4
+ {id:"40_raphael",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("拉斐尔",50,1034);c.restore();}}},
5
+ {id:"41_michelangelo",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("米开朗基罗",50,1034);c.restore();}}},
6
+ {id:"42_guardi",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("瓜尔迪",50,1034);c.restore();}}},
7
+ {id:"43_lorenzetti",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("洛伦采蒂",50,1034);c.restore();}}},
8
+ {id:"44_cortona",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("科尔托纳",50,1034);c.restore();}}},
9
+ {id:"45_renoir",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("雷诺阿",50,1034);c.restore();}}},
10
+ {id:"46_pissarro",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("毕沙罗",50,1034);c.restore();}}},
11
+ {id:"47_sisley",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("西斯莱",50,1034);c.restore();}}},
12
+ {id:"48_gauguin",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("高更",50,1034);c.restore();}}},
13
+ {id:"49_chagall",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("夏加尔",50,1034);c.restore();}}},
14
+ {id:"50_ma_yuan",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("马远",50,1034);c.restore();}}},
15
+ {id:"51_persian_miniature",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("波斯细密画",50,1034);c.restore();}}},
16
+ {id:"52_south_park",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("南方公园",50,1034);c.restore();}}},
17
+ {id:"54_ghibli_lab",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("吉卜力方向",50,1034);c.restore();}}},
18
+ {id:"53_pixar",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("皮克斯方向",50,1034);c.restore();}}},
19
+ {id:"55_dunhuang",dur:6,counter:{draw(c){c.save();c.font='25px sans-serif';c.textAlign='left';c.fillStyle='rgba(25,28,23,.75)';c.fillRect(34,1002,230,46);c.fillStyle='#fff9e9';c.fillText("敦煌壁画",50,1034);c.restore();}}}
20
+ ];
@@ -0,0 +1,19 @@
1
+ {
2
+ "grammar": "t1_3b1b",
3
+ "duration": 10,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "ink",
8
+ "cues": [
9
+ { "at": 0.0, "kind": "line", "text": "损失 L(θ)", "data": { "values": [9, 5.8, 3.4, 1.8, 1.0, 0.8, 1.0, 1.8, 3.4, 5.8, 9], "color": "BLUE_C" } },
10
+ { "at": 1.8, "kind": "equation", "data": { "tokens": [
11
+ { "s": "L", "it": true, "col": "BLUE_C" }, { "s": "(", "gap": 4 }, { "s": "θ", "it": true, "col": "YELLOW", "gap": 4 }, { "s": ")" } ] } },
12
+ { "at": 3.4, "kind": "equation", "data": { "tokens": [
13
+ { "s": "θ", "it": true, "col": "YELLOW" }, { "s": "←", "gap": 18 }, { "s": "θ", "it": true, "col": "YELLOW", "gap": 14 },
14
+ { "s": "−", "gap": 14 }, { "s": "η", "it": true, "col": "GREEN_C", "gap": 8 }, { "s": "∇", "gap": 4 }, { "s": "L", "it": true, "col": "BLUE_C" } ] } },
15
+ { "at": 5.0, "kind": "point", "text": "沿坡度反方向走一小步" },
16
+ { "at": 6.4, "kind": "line", "text": "训练中的损失", "data": { "values": [9, 6.2, 4.4, 3.2, 2.4, 1.9, 1.55, 1.3, 1.15, 1.05, 1.0], "color": "TEAL_C" } },
17
+ { "at": 8.2, "kind": "highlight", "data": { "target": "equation" } }
18
+ ]
19
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "grammar": "t2_keynote_ui",
3
+ "duration": 8,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "ink",
8
+ "cues": [
9
+ { "at": 0.0, "kind": "card", "text": "for 循环累加", "sub": "一百万个整数", "image": "assets/终端_for循环求和.png" },
10
+ { "at": 1.4, "kind": "highlight", "text": "318 毫秒", "data": { "rect": [0.292, 0.212, 0.115, 0.07] } },
11
+ { "at": 3.2, "kind": "card", "text": "换成 sum()", "image": "assets/终端_sum求和.png" },
12
+ { "at": 4.3, "kind": "highlight", "text": "20.9 毫秒", "data": { "rect": [0.292, 0.153, 0.128, 0.07] } },
13
+ { "at": 6.4, "kind": "number", "text": "同一件事快了", "data": { "value": 15.2, "decimals": 1, "suffix": " 倍", "over": true } }
14
+ ]
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "grammar": "t2_keynote_ui",
3
+ "duration": 10,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "navy",
8
+ "cues": [
9
+ { "at": 0.0, "kind": "card", "text": "一份 JSON 出的图", "sub": "数据:英伟达 10-K 年报", "image": "assets/财经图表_横屏.png" },
10
+ { "at": 1.8, "kind": "highlight", "data": { "rect": [0.648, 0.245, 0.104, 0.105] } },
11
+ { "at": 4.0, "kind": "card", "text": "竖屏换一套排版", "sub": "同一份数据", "image": "assets/营收图_竖屏.png" },
12
+ { "at": 6.6, "kind": "number", "text": "可换的动画语法", "data": { "value": 8, "suffix": " 种" } },
13
+ { "at": 8.0, "kind": "card", "text": "时长精确到帧", "sub": "帧数 = 时长 × 帧率", "data": { "icon": "clock" } }
14
+ ]
15
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "grammar": "t3_finance_chart",
3
+ "duration": 7,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "paper",
8
+ "data": {
9
+ "title": "英伟达营收涨到 7.8 倍",
10
+ "unit": "年营收,十亿美元(2021–2025 财年)",
11
+ "source": "来源:英伟达 2021–2025 财年 10-K 年报(财年截至次年 1 月)",
12
+ "chart": "bar",
13
+ "decimals": 1,
14
+ "series": [
15
+ { "label": "FY2021", "value": 16.68 },
16
+ { "label": "FY2022", "value": 26.91 },
17
+ { "label": "FY2023", "value": 26.97 },
18
+ { "label": "FY2024", "value": 60.92 },
19
+ { "label": "FY2025", "value": 130.50 }
20
+ ],
21
+ "highlight": { "index": 4, "text": "7.8 倍", "sub": "130.5 ÷ 16.7" }
22
+ },
23
+ "cues": [
24
+ { "at": 0.3, "kind": "bar", "data": { "index": [0, 1, 2] } },
25
+ { "at": 1.7, "kind": "bar", "data": { "index": 3 } },
26
+ { "at": 2.7, "kind": "bar", "data": { "index": 4 } },
27
+ { "at": 4.4, "kind": "highlight" }
28
+ ]
29
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "grammar": "y1_kurzgesagt",
3
+ "duration": 8,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "navy",
8
+ "data": { "center": "一滴海水" },
9
+ "cues": [
10
+ { "at": 0.3, "kind": "point", "text": "浮游生物" },
11
+ { "at": 1.3, "kind": "enter", "text": "一只硅藻", "data": { "index": 0 } },
12
+ { "at": 3.3, "kind": "point", "text": "硅质外壳" },
13
+ { "at": 4.3, "kind": "point", "text": "叶绿体" },
14
+ { "at": 5.6, "kind": "highlight", "data": { "index": 1 } }
15
+ ]
16
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "grammar": "y2_vox",
3
+ "duration": 9,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "wood",
8
+ "cues": [
9
+ { "at": 0.0, "kind": "image", "image": "assets/K线图_横屏.png", "text": "日 K 线·示意数据" },
10
+ { "at": 1.8, "kind": "highlight", "data": { "rect": [0.74, 0.22, 0.22, 0.11] } },
11
+ { "at": 4.2, "kind": "image", "image": "assets/营收图_竖屏.png", "text": "同一图表的竖屏版" },
12
+ { "at": 6.6, "kind": "title", "text": "两张图都是代码画的" }
13
+ ]
14
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "grammar": "y3_whiteboard",
3
+ "duration": 10,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "chalk",
8
+ "cues": [
9
+ { "at": 0.0, "kind": "draw", "text": "25 分钟", "data": { "icon": "clock" } },
10
+ { "at": 1.5, "kind": "point", "text": "这段时间只做一件事" },
11
+ { "at": 2.8, "kind": "draw", "text": "不切换", "data": { "icon": "target" } },
12
+ { "at": 4.1, "kind": "point", "text": "铃响就休息 5 分钟" },
13
+ { "at": 5.4, "kind": "draw", "text": "换口气", "data": { "icon": "bulb" } },
14
+ { "at": 6.8, "kind": "point", "text": "四轮后长休一次" },
15
+ { "at": 8.2, "kind": "highlight", "data": { "target": "draw", "index": 0 } }
16
+ ]
17
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "grammar": "y4_storytime",
3
+ "duration": 8.5,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "snow",
8
+ "data": {
9
+ "character": {
10
+ "frames": {
11
+ "rest": "../../../角色帧库/<你的角色>/rest.png",
12
+ "talk": "../../../角色帧库/<你的角色>/talk.png",
13
+ "point": "../../../角色帧库/<你的角色>/point.png",
14
+ "shrug": "../../../角色帧库/<你的角色>/shrug.png"
15
+ }
16
+ }
17
+ },
18
+ "cues": [
19
+ {
20
+ "at": 0.1,
21
+ "kind": "insert",
22
+ "text": "3000行",
23
+ "sub": "帮我做个网站",
24
+ "data": {
25
+ "hit": 1.6,
26
+ "count": 3000,
27
+ "unit": "行"
28
+ }
29
+ },
30
+ {
31
+ "at": 2.8,
32
+ "kind": "react"
33
+ },
34
+ {
35
+ "at": 4.1,
36
+ "kind": "point",
37
+ "text": "第一次让 AI 写代码,",
38
+ "sub": "这些……贴哪儿?",
39
+ "data": {
40
+ "pose": "point"
41
+ }
42
+ },
43
+ {
44
+ "at": 6.6,
45
+ "kind": "highlight",
46
+ "data": {
47
+ "mark": "?"
48
+ }
49
+ }
50
+ ],
51
+ "_note": "y4 必须用你自己的角色帧库(AI 生帧,见 references/10-角色.md 与 角色精细度.md)。把 <你的角色> 换成你的帧库;没有生图能力就别用 y4。"
52
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "grammar": "y5_kinetic_type",
3
+ "duration": 7.5,
4
+ "fps": 30,
5
+ "width": 1920,
6
+ "height": 1080,
7
+ "theme": "poster",
8
+ "cues": [
9
+ { "at": 0.7, "kind": "number", "text": "一句话换来的代码", "data": { "value": 3000, "suffix": " 行" } },
10
+ { "at": 2.6, "kind": "point", "text": "补全", "sub": "以前它补一行", "data": { "label": "从前", "key": "一行" } },
11
+ { "at": 4.3, "kind": "point", "text": "代理", "sub": "现在它做完整件事", "data": { "label": "现在", "key": "整件事" } },
12
+ { "at": 5.6, "kind": "highlight", "data": { "word": "代理" } }
13
+ ]
14
+ }
@@ -0,0 +1,42 @@
1
+ <!doctype html>
2
+ <html><head><meta charset="utf-8"><title>Art History Speedrun · 复刻</title>
3
+ <style>
4
+ html,body{margin:0;background:#111;overflow:hidden}
5
+ canvas{display:block;width:100vw;height:auto;max-width:1920px}
6
+ #ui{position:fixed;left:8px;bottom:8px;color:#ddd;font:12px monospace}
7
+ body.render #ui{display:none}
8
+ </style></head>
9
+ <body>
10
+ <canvas id="c" width="1920" height="1080"></canvas>
11
+ <div id="ui"><input id="scrub" type="range" min="0" max="60" step="0.0166667" value="0" style="width:600px"> <span id="tt"></span> <button id="play">play</button></div>
12
+ <script src="lib/fonts.js"></script>
13
+ <script src="lib/util.js"></script>
14
+ <script src="lib/paint.js"></script>
15
+ <script src="lib/brush.js"></script>
16
+ <script src="lib/render.js"></script>
17
+ <script src="lib/post.js"></script>
18
+ <script src="lib/rig.js"></script>
19
+ <script src="lib/rig_huashu.js"></script>
20
+ <script src="lib/kit.js"></script>
21
+ <script src="lib/medium.js"></script>
22
+ <!-- YouTube 解说语法库(references/09):动效时间、相机、图解形变、文字、图表、发布会界面、拼贴、卡通 -->
23
+ <script src="lib/motion.js"></script>
24
+ <script src="lib/camera.js"></script>
25
+ <script src="lib/diagram.js"></script>
26
+ <script src="lib/typo.js"></script>
27
+ <script src="lib/chart.js"></script>
28
+ <script src="lib/ui.js"></script>
29
+ <script src="lib/collage.js"></script>
30
+ <script src="lib/media.js"></script>
31
+ <script src="lib/toon.js"></script>
32
+ <script>
33
+ // 选片子:默认 eras.js;?film=<名> → eras_<名>.js(如 ?film=gallery = 35 种风格样片);?film=demos/<名> → demos/<名>/eras.js(8 种 YouTube 语法示范)。
34
+ // render.py / qa.py 用 --film 传。
35
+ (() => { const f = new URLSearchParams(location.search).get('film');
36
+ const src = f && /^demos\/[a-z0-9_]+$/.test(f) ? `${f}/eras.js` : f && /^[a-z0-9_]+$/.test(f) ? `eras_${f}.js` : 'eras.js';
37
+ document.write(`<script src="${src}"><\/script>`); })();
38
+ </script>
39
+ <script src="transitions.js"></script>
40
+ <script src="scenes/index.js"></script>
41
+ <script src="engine.js"></script>
42
+ </body></html>
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright © 2010-2023 three.js authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ // 笔与水:毛笔飞白、两头尖单笔、墨晕晕染、长弯流线笔、水彩洗染、厚涂鬃毛笔、剪刀折线。全部挂在 PAINT(P)上。
2
+ // 来源:迁移测试 A(水墨/蒙克)、B(吉卜力)、C(马蒂斯)、D(伦勃朗)。确定性:所有随机都走种子。
3
+ // P.brush(c, pts, opt) 毛笔:笔压起收+湿笔芯+逐根笔毛飞白,可按 reveal 写出(水墨、书法、白描衣纹)
4
+ // P.leaf(c, x, y, ang, L, w) 两头尖的单笔(竹叶、兰叶、鸟翅、虾尾)
5
+ // P.inkWash(c, key, grow, fn, o) 墨晕:形状先画到 scratch,再「外晕+本体」两遍模糊 multiply 到纸上,grow 0→1 是洇开
6
+ // P.flowSeeds / P.flowLines 长弯流线笔:种子沿随时间扭动的方向场积分成 200–300px 的长笔(蒙克,不是梵高的短笔)
7
+ // P.deform / P.watercolor 水彩洗染:中点位移变形多边形 × 多层低 α multiply + 每层淡描边=水痕边(吉卜力背景)
8
+ // P.rectPts / P.ellipsePts 洗染常用的多边形点列
9
+ // P.impasto(c, pts, w, col, hi) 厚涂鬃毛笔:一笔=若干平行细鬃(伦勃朗高光、梵高厚涂补笔)
10
+ // P.cut(pts, seed, step, amp) 剪刀折线:沿轮廓每 step px 取点+抖动、直线相连(马蒂斯剪纸、剪影、木刻)
11
+ (() => {
12
+ let W = 1920, H = 1080; U.onStage((w, h) => { W = w; H = h; }); // 画布尺寸跟 U.setStage 走(默认 1920×1080)
13
+ const P = window.PAINT;
14
+ const { clamp, lerp, rng, ss } = U;
15
+
16
+ // ---------- 毛笔 ----------
17
+ // pts 控制点(会 Catmull-Rom 加密再按 4px 等弧长重采样——不重采样的话飞白噪声频率随笔画长短变)
18
+ // w 最大宽;tone 墨色透明度(焦 .9/浓 .8/重 .6/淡 .35/清 .15);dry 0..1 飞白程度(越到笔尾越干);reveal 0..1 写出比例
19
+ // head/tail 起笔/收笔占比;bristles 笔毛数;col 墨色 [r,g,b];per 加密倍数
20
+ // 坑:笔毛必须每根一条连续 path(butt 头);按小段单独 stroke+round 头,接头处会叠出一节节深点,像竹节/麻绳。
21
+ P.brush = (c, pts, { w = 10, tone = 0.85, dry = 0.45, seed = 1, reveal = 1, bristles = 8, col = [16, 14, 12], head = 0.12, tail = 0.4, per = 5 } = {}) => {
22
+ if (reveal <= 0) return;
23
+ const D = KIT.resample(KIT.densify(pts, per), 4), n = D.length, m = Math.max(2, Math.ceil(n * clamp(reveal)));
24
+ const r = rng(seed * 7919);
25
+ const prof = q => ss(0, head, q) * (1 - 0.7 * ss(1 - tail, 1, q)) * (0.85 + 0.15 * P.noise(seed * 3.3, q * 6)) + 0.08;
26
+ c.save(); c.lineCap = 'butt'; c.lineJoin = 'round';
27
+ const N = [];
28
+ for (let i = 0; i < n; i++) { const A = D[Math.max(0, i - 1)], B = D[Math.min(n - 1, i + 1)]; let dx = B[0] - A[0], dy = B[1] - A[1]; const d = Math.hypot(dx, dy) || 1; N.push([-dy / d, dx / d]); }
29
+ const core = q => w * prof(q) * (1 - dry * 0.85 * Math.pow(q, 0.8));
30
+ // 湿的笔芯:整笔都有,越往后越细越淡(墨在用完)
31
+ c.fillStyle = `rgba(${col[0]},${col[1]},${col[2]},${tone * 0.72})`; c.fill(KIT.ribbon(D.slice(0, m), (q, i) => core(i / (n - 1))));
32
+ // 笔毛:每根一条连续线,按噪声断开(飞白=笔毛之间露出的纸),越到笔尾断得越多
33
+ for (let k = 0; k < bristles; k++) {
34
+ const off = (k / (bristles - 1) - 0.5) * 0.95, bw = w / bristles * (0.9 + r() * 0.9), ph = r() * 50;
35
+ c.strokeStyle = `rgba(${col[0]},${col[1]},${col[2]},${tone * (0.45 + r() * 0.35)})`; c.lineWidth = Math.max(0.7, bw);
36
+ c.beginPath(); let on = false;
37
+ for (let i = 0; i < m; i++) {
38
+ const q = i / (n - 1), dryness = dry * Math.pow(q, 1.2);
39
+ const vis = P.noise(k * 1.9 + ph, i * 0.09) * 0.5 + 0.5 > dryness * 0.95 + 0.05;
40
+ const o = off * w * prof(q), x = D[i][0] + N[i][0] * o, y = D[i][1] + N[i][1] * o;
41
+ if (vis) { on ? c.lineTo(x, y) : c.moveTo(x, y); on = true; } else on = false;
42
+ }
43
+ c.stroke();
44
+ }
45
+ c.restore();
46
+ };
47
+ // 两头尖的单笔(个字竹叶、兰叶描、鸟翅):沿 ang 方向长 L,最宽 wmax,微弯
48
+ P.leaf = (c, x, y, ang, L, wmax, tone = 0.88, col = [16, 14, 12]) => {
49
+ if (!Array.isArray(col)) col = [16, 14, 12];
50
+ const pts = []; for (let i = 0; i <= 10; i++) { const q = i / 10, bend = Math.sin(q * Math.PI) * 0.08 * L; pts.push([x + Math.cos(ang) * L * q - Math.sin(ang) * bend, y + Math.sin(ang) * L * q + Math.cos(ang) * bend]); }
51
+ c.fillStyle = `rgba(${col[0]},${col[1]},${col[2]},${tone})`; c.fill(KIT.ribbon(pts, q => wmax * Math.pow(Math.sin(Math.PI * Math.min(1, q * 1.1)), 0.7) + 0.6));
52
+ };
53
+
54
+ // ---------- 墨晕 / 晕染层 ----------
55
+ // fn(g) 在 scratch 上画形状(实色即可);grow 0..1 控制洇开:外晕 blur 随 grow 变宽,本体 blur 随 grow 收紧。
56
+ // halo 外晕模糊半径、haloA 外晕透明度、blur 本体模糊、alpha 整体透明度。返回 scratch(可再拿来做留白挖洞)。
57
+ // 坑:multiply 是透明的,背后的墨线会透过晕染(窗台线透过猫耳)。先在剪影里补一层纸色(clip 后 drawImage 纸)再上晕染=「先留出这块地方」。
58
+ P.inkWash = (c, key, grow, fn, { blur = 2, halo = 10, haloA = 0.22, alpha = 1 } = {}) => {
59
+ const S = P.scratch('inkw_' + key), g = S.getContext('2d'); g.reset(); fn(g);
60
+ c.save(); c.globalCompositeOperation = 'multiply';
61
+ if (grow > 0) {
62
+ c.globalAlpha = haloA * alpha * grow; c.filter = `blur(${(halo * (0.4 + grow)).toFixed(2)}px)`; c.drawImage(S, 0, 0);
63
+ c.globalAlpha = alpha * ss(0, 0.6, grow); c.filter = `blur(${(blur * (1.6 - grow * 0.6)).toFixed(2)}px)`; c.drawImage(S, 0, 0);
64
+ }
65
+ c.filter = 'none'; c.restore();
66
+ return S;
67
+ };
68
+
69
+ // ---------- 长弯流线笔(蒙克《呐喊》式表现主义) ----------
70
+ // 种子:cell 抖动网格,每个种子带两个随机数 [x, y, r1, r2]
71
+ P.flowSeeds = (cell = 24, seed = 5) => { const r = rng(seed), o = []; for (let y = -10; y < H + 10; y += cell) for (let x = -10; x < W + 10; x += cell) o.push([x + (r() - .5) * cell, y + (r() - .5) * cell, r(), r()]); return o; };
72
+ // 每个种子:region(x,y,t) 判区 → 取该区参数 params[reg] = [steps, step, width, alpha] → 沿 angle(x,y,t,reg) 积分;越出本区就停(区域边界干净)。
73
+ // color(reg, sx, sy, r1, t) 返回 [r,g,b]。不描边、半透明叠色——蒙克是薄涂长笔;照搬梵高的深色描边会变成一节节毛毛虫短笔。
74
+ // 底下先铺同色系平涂,笔缝里露出的是同色而不是黑。长度:steps 26–42、step 6–8,一根笔走 200–300px。
75
+ P.flowLines = (c, seeds, { t = 0, region, angle, color, params, extraSteps = 8 }) => {
76
+ c.lineCap = 'round'; c.lineJoin = 'round';
77
+ for (const [sx, sy, r1, r2] of seeds) {
78
+ const reg = region(sx, sy, t); const pr = params[reg]; if (!pr) continue; const [steps, step, width, alpha] = pr;
79
+ const col = color(reg, sx, sy, r1, t);
80
+ let x = sx, y = sy; c.beginPath(); c.moveTo(x, y);
81
+ const n = steps + ((r2 * extraSteps) | 0);
82
+ for (let i = 0; i < n; i++) { const a = angle(x, y, t, reg); x += Math.cos(a) * step; y += Math.sin(a) * step; if (region(x, y, t) !== reg) break; c.lineTo(x, y); }
83
+ c.strokeStyle = P.rgb(col, alpha); c.lineWidth = width * (0.7 + r2 * 0.6); c.stroke();
84
+ }
85
+ };
86
+
87
+ // ---------- 水彩洗染(吉卜力式背景美术) ----------
88
+ // 中点位移:每轮在每条边中点沿法向推一个近似高斯量(3 个均匀数相加),幅度与边长成比例,每轮 amp×0.6
89
+ P.deform = (pts, depth, amp, r) => {
90
+ let out = pts;
91
+ for (let d = 0; d < depth; d++) {
92
+ const n = [];
93
+ for (let i = 0; i < out.length; i++) {
94
+ const a = out[i], b = out[(i + 1) % out.length], len = Math.hypot(b[0] - a[0], b[1] - a[1]);
95
+ const g = (r() + r() + r() - 1.5) * amp * Math.min(1, len / 80), nx = -(b[1] - a[1]) / (len || 1), ny = (b[0] - a[0]) / (len || 1);
96
+ n.push(a, [(a[0] + b[0]) / 2 + nx * g, (a[1] + b[1]) / 2 + ny * g]);
97
+ }
98
+ out = n; amp *= 0.6;
99
+ }
100
+ return out;
101
+ };
102
+ const fillPts = (g, pts) => { g.beginPath(); pts.forEach((q, i) => i ? g.lineTo(q[0], q[1]) : g.moveTo(q[0], q[1])); g.closePath(); };
103
+ // 一次洗染:基形 deform 3 轮;每层再 deform 2 轮、低 α 填(multiply = 透明颜料叠色)、α=edge 描 1.6px 同色边(颜料在边缘沉积 = 水痕边)。
104
+ // fill 可传渐变;不透明的白(窗框、云、桌布)用 blend:'source-over'、alpha .9。
105
+ // 常用:大墙 layers5 α.4 amp12;色晕 layers3 α.045 amp40;木板 layers3 α.5 amp3 edge.14。
106
+ // 坑:暗部用大幅变形多边形(amp 60),别用矩形拼——矩形接缝处会出硬边。色晕 α 别超 .05×3 层,否则房间像发霉的羊皮纸。
107
+ P.watercolor = (g, pts, color, { layers = 7, alpha = 0.13, amp = 10, seed = 1, edge = 0.1, blend = 'multiply', fill } = {}) => {
108
+ const r = rng(seed * 7919 + 13), base = P.deform(pts, 3, amp, r);
109
+ g.save(); g.globalCompositeOperation = blend; g.lineJoin = 'round';
110
+ for (let k = 0; k < layers; k++) {
111
+ const q = P.deform(base, 2, amp * 0.55, r);
112
+ fillPts(g, q); g.globalAlpha = alpha; g.fillStyle = fill || color; g.fill();
113
+ if (edge) { g.globalAlpha = edge; g.strokeStyle = color; g.lineWidth = 1.6; g.stroke(); }
114
+ }
115
+ g.restore();
116
+ };
117
+ P.rectPts = (x, y, w, h) => [[x, y], [x + w, y], [x + w, y + h], [x, y + h]];
118
+ P.ellipsePts = (cx, cy, rx, ry, n = 18) => Array.from({ length: n }, (_, i) => [cx + Math.cos(i / n * Math.PI * 2) * rx, cy + Math.sin(i / n * Math.PI * 2) * ry]);
119
+
120
+ // ---------- 厚涂鬃毛笔 ----------
121
+ // 一笔 = round(w/1.3) 条平行细鬃(亮度各自 ±17 抖、两端随机缩 0–25%)+下侧一条淡投影+上侧零星亮点 → 读成「被刷开的一坨厚颜料」。
122
+ // 只用在受光处(金发、领口、瓷器、珍珠、白毛)。坑:画成「暗影+主笔+亮脊」三条整齐的线会读成白色贴纸/创可贴。
123
+ P.impasto = (c, pts, w, col, hi) => {
124
+ const base = col.startsWith('#') ? P.hex(col) : [240, 230, 210];
125
+ const A = pts[0], B = pts[pts.length - 1], ang = Math.atan2(B[1] - A[1], B[0] - A[0]), nx = -Math.sin(ang), ny = Math.cos(ang);
126
+ const r = rng((A[0] * 13 + A[1] * 7) | 0), nb = Math.max(3, Math.round(w / 1.3));
127
+ c.save(); c.lineCap = 'round';
128
+ c.strokeStyle = 'rgba(40,18,6,.28)'; c.lineWidth = w * 0.9; c.beginPath(); pts.forEach((p, i) => i ? c.lineTo(p[0] + nx * w * 0.45 + 1, p[1] + ny * w * 0.45 + 1.5) : c.moveTo(p[0] + nx * w * 0.45 + 1, p[1] + ny * w * 0.45 + 1.5)); c.stroke();
129
+ for (let k = 0; k < nb; k++) {
130
+ const o = (k / (nb - 1) - 0.5) * w, s0 = r() * 0.25, s1 = 1 - r() * 0.25, sh = (r() - 0.5) * 34 + (k < nb / 2 ? 14 : -10);
131
+ c.strokeStyle = P.rgb([base[0] + sh, base[1] + sh, base[2] + sh * 0.8], 0.55 + r() * 0.4); c.lineWidth = 1.4 + r() * 1.2; c.beginPath();
132
+ const n = pts.length - 1; for (let i = 0; i <= 8; i++) { const q = lerp(s0, s1, i / 8), j = Math.min(n - 1, Math.floor(q * n)), f = q * n - j, x = lerp(pts[j][0], pts[j + 1][0], f) + nx * o, y = lerp(pts[j][1], pts[j + 1][1], f) + ny * o; i ? c.lineTo(x, y) : c.moveTo(x, y); }
133
+ c.stroke();
134
+ }
135
+ c.fillStyle = hi || 'rgba(255,250,232,.7)'; for (let k = 0; k < 2 + w / 3; k++) { const q = r(), x = lerp(A[0], B[0], q) - nx * w * 0.3, y = lerp(A[1], B[1], q) - ny * w * 0.3; c.beginPath(); c.arc(x, y, 0.9 + r(), 0, Math.PI * 2); c.fill(); }
136
+ c.restore();
137
+ };
138
+
139
+ // ---------- 剪刀折线 ----------
140
+ // 把一圈(闭合)点列按 step 重采样,每点加 ±amp 种子抖动,直线相连 → 手剪纸的边。RIG 部件可用 path.pts 喂进来重剪。
141
+ P.cut = (pts, seed, step = 18, amp = 2.5) => {
142
+ const r = rng(seed), out = [];
143
+ for (let i = 0; i < pts.length; i++) {
144
+ const a = pts[i], b = pts[(i + 1) % pts.length], L = Math.hypot(b[0] - a[0], b[1] - a[1]), n = Math.max(1, Math.round(L / step));
145
+ for (let k = 0; k < n; k++) { const q = k / n; out.push([lerp(a[0], b[0], q) + (r() - 0.5) * amp * 2, lerp(a[1], b[1], q) + (r() - 0.5) * amp * 2]); }
146
+ }
147
+ return U.poly(out);
148
+ };
149
+ })();
@@ -0,0 +1,99 @@
1
+ // 世界相机 CAM:解说片不是「一段段画+转场」,而是「一张连续的世界画布+一台相机」。
2
+ // 铁律:相机只由全片时间(或本段时间)算出来,场景每帧按相机矢量重画——缩放转场永远清楚,不会把 1920 的位图放大 20 倍糊掉;
3
+ // 引擎在转场窗口里会把上一段继续按 t 画进 A 缓冲,所以「上一镜的相机继续推」天然成立(live 推进)。
4
+ // cam = { x, y, z, r }:世界点 (x,y) 落在画面中心,缩放 z(>1 推近),绕中心旋转 r(弧度)。缺省 x=960,y=540,z=1,r=0。
5
+ (() => {
6
+ let W = 1920, H = 1080; U.onStage((w, h) => { W = w; H = h; }); // 画布尺寸跟 U.setStage 走(默认 1920×1080)
7
+ const { clamp, lerp } = U;
8
+ const CAM = window.CAM = {};
9
+ const n = cam => ({ x: cam.x ?? W / 2, y: cam.y ?? H / 2, z: cam.z ?? 1, r: cam.r || 0 });
10
+
11
+ // 把相机变换作用到 c 上(不 save/restore,调用方负责)
12
+ CAM.apply = (c, cam) => { const k = n(cam); c.translate(W / 2, H / 2); if (k.r) c.rotate(k.r); c.scale(k.z, k.z); c.translate(-k.x, -k.y); };
13
+ // 在相机下画 fn(c),自动 save/restore
14
+ CAM.with = (c, cam, fn) => { c.save(); CAM.apply(c, cam); fn(c); c.restore(); };
15
+ // 世界 → 屏幕 / 屏幕 → 世界
16
+ CAM.toScreen = (cam, x, y) => { const k = n(cam); let dx = (x - k.x) * k.z, dy = (y - k.y) * k.z; if (k.r) { const cs = Math.cos(k.r), sn = Math.sin(k.r); [dx, dy] = [dx * cs - dy * sn, dx * sn + dy * cs]; } return [W / 2 + dx, H / 2 + dy]; };
17
+ CAM.toWorld = (cam, sx, sy) => { const k = n(cam); let dx = sx - W / 2, dy = sy - H / 2; if (k.r) { const cs = Math.cos(-k.r), sn = Math.sin(-k.r); [dx, dy] = [dx * cs - dy * sn, dx * sn + dy * cs]; } return [k.x + dx / k.z, k.y + dy / k.z]; };
18
+
19
+ // ---------- 缩放 ----------
20
+ // 对数插值:z0→z1 每帧放大同样的倍数,观众感到「匀速穿越尺度」;线性插值会前慢后猛。所有推拉缩放都用它。
21
+ CAM.zlerp = (z0, z1, e) => z0 * Math.pow(z1 / z0, e);
22
+ // 「从一个半径 r 的圆形开口穿进去,开口要在转场结束前盖满屏」需要的最小放大倍数 = 开口中心到最远屏幕角的距离 / r。
23
+ // 开口在画面中心时 ≈ 1101/r(卡片里写的「放大倍数 ≥ 1100/半径」)。小了,最后一帧开口没盖满会跳。
24
+ CAM.coverZoom = (r, cx = W / 2, cy = H / 2) => Math.max(Math.hypot(cx, cy), Math.hypot(W - cx, cy), Math.hypot(cx, H - cy), Math.hypot(W - cx, H - cy)) / r;
25
+ // 让世界点 (wx,wy) 在缩放 z 下出现在屏幕 (sx,sy):返回相机。推近某物时让它平滑移到画面中心就靠它。
26
+ CAM.anchor = (wx, wy, z, sx, sy) => ({ x: wx - (sx - W / 2) / z, y: wy - (sy - H / 2) / z, z });
27
+ // 推进某个世界点:从 base 相机出发,缩放按对数插值到 Z(进度 e);该点在屏幕上从原位置移向画面中心(进度 k,默认 = e;k=0 = 绕它原地推)。
28
+ // 用于「钻进镜头」(y1)、「推满照片再硬切」(y2)、「推进最新数据点」(t3,k=0)。
29
+ CAM.pushTo = (base, wx, wy, Z, e, k = e, to = [W / 2, H / 2]) => {
30
+ const s0 = CAM.toScreen(base, wx, wy), z = CAM.zlerp(n(base).z, Z, e);
31
+ return CAM.anchor(wx, wy, z, lerp(s0[0], to[0], k), lerp(s0[1], to[1], k));
32
+ };
33
+
34
+ // ---------- 关键帧 ----------
35
+ // keys = [{t, x, y, z, r, ease}];ease 作用在「从上一键到这一键」,默认 cubicInOut;z 用对数插值。
36
+ // 沿路径平移(Vox 桌面、白板上的平移/甩镜)就是一串关键帧:平滑移动用 sineInOut,远跳用 cubicInOut+运动模糊,沿线追用 MO.longTail。
37
+ CAM.at = (keys, t) => {
38
+ if (t <= keys[0].t) return { ...keys[0] };
39
+ for (let i = 1; i < keys.length; i++) {
40
+ const a = keys[i - 1], b = keys[i];
41
+ if (t <= b.t) { const e = (b.ease || MO.cubicInOut)(MO.seg(t, a.t, b.t));
42
+ return { x: lerp(a.x, b.x, e), y: lerp(a.y, b.y, e), z: CAM.zlerp(a.z, b.z, e), r: lerp(a.r || 0, b.r || 0, e), v: e }; }
43
+ }
44
+ return { ...keys[keys.length - 1] };
45
+ };
46
+
47
+ // ---------- 镜头动作表(解说片用它;什么时候用哪一种见 references/镜头语言.md) ----------
48
+ // 默认是过渡类动作:缓推 push、找下一件 glide、拉远 pull(0.6–1.5s,sineInOut / 长尾);重音类:快推 punch、快移 pan、砸入 slam,
49
+ // 留给揭晓、数字、反转。时长在 MO.PULSE。一支片子里推近要有来有回:推进去之后用 pull、glide 或硬切换开,别一镜接一镜只往里推。
50
+ // CAM.track(base, events, t) → cam:base 是起始相机 {x, y, z};events 是按时间排的镜头动作,相机只由 t 算出来(确定性,可跳帧)。
51
+ // 每个动作从「上一个动作结束时的相机」出发,到 at + dur 落定;两个动作挨太近(上一个没走完下一个就开始)时,下一个从上一个此刻的位置接着走。
52
+ // {at, kind: 'push', k?, x?, y?, dur?} 缓推(不写 kind 就是它):z 乘以 (1+k)(k 默认 0.2),0.9s sineInOut;给 x, y 就同时把这个世界点移到画面中心。
53
+ // {at, kind: 'pull', k?, x?, y?, z?, dur?} 拉远:z 乘以 (1+k)(k 默认 -1/6,正好抵消一次默认缓推;回全貌最稳是写 z: base.z),1.1s 长尾。推进去之后回到全貌、段落收束用它。
54
+ // {at, kind: 'glide', x, y, dur?} 找下一件:缩放不变,画面中心平滑移到世界点 (x, y),0.7s sineInOut。同一面墙上从一件移到下一件。
55
+ // {at, kind: 'to', x?, y?, z?, dur?, ease?} 推到指定相机(推近截图上的一块:z 用 MD.zoomFor 算);默认 0.9s sineInOut,写 dur: MO.PULSE.punch 就是重音。
56
+ // {at, kind: 'punch', k?, x?, y?, dur?} 快推(重音):0.28s cubicInOut,k 默认 0.25(0.18–0.32)。k 可以写负数(快拉)。
57
+ // {at, kind: 'pan', x, y, dur?} 快移(重音):同倍率 0.30s。视线突然转移、喜剧节奏用。
58
+ // {at, kind: 'slam', dur?, from?} 整帧砸入:z 额外乘 MO.slamK(1.55→0.94→1.0,0.17s)。大字、数字、盖章落下那一帧用。
59
+ // {at, kind: 'shake', amp?, dur?} 震镜:dur(默认 4 帧 ≈ 0.13s)内上下抖 amp 像素(默认 14),和砸入、盖章同帧用,一支片子别超过两次。
60
+ // {at, kind: 'cut', x?, y?, z?} 硬切到这台相机(不过渡)。换景别不一定要运镜,跳近一档就是硬切。
61
+ // 返回的 cam 带 moving: true/false(这一刻是否在运动),给运动模糊、标签显隐用。
62
+ CAM.track = (base, events, t) => {
63
+ let cam = { x: base.x ?? W / 2, y: base.y ?? H / 2, z: base.z ?? 1 }, zMul = 1, dy = 0, moving = false;
64
+ const evs = [...events].sort((a, b) => a.at - b.at);
65
+ for (const e of evs) {
66
+ if (t < e.at) break;
67
+ const P = MO.PULSE, kind = e.kind || 'push';
68
+ if (kind === 'cut') { cam = { x: e.x ?? cam.x, y: e.y ?? cam.y, z: e.z ?? cam.z }; continue; }
69
+ if (kind === 'slam') { const d = e.dur || P.slam, k = (t - e.at) / d; if (k < 1) { zMul *= MO.slamK(k, e.from); moving = true; } continue; }
70
+ if (kind === 'shake') { const d = e.dur || 4 / 30, k = (t - e.at) / d; if (k < 1) { dy += (e.amp ?? 14) * Math.sin(k * Math.PI * 4) * (1 - k) / cam.z; moving = true; } continue; }
71
+ const D = { pan: P.pan, punch: P.punch, glide: P.glide, pull: P.pull, push: P.push, to: P.push }[kind] ?? P.push;
72
+ const E = kind === 'punch' || kind === 'pan' || (e.dur && e.dur <= 0.35) ? MO.cubicInOut : kind === 'pull' ? MO.longTail : MO.sineInOut; // 写了重音时长的 to 也用快推的曲线
73
+ const d = e.dur || D, p = (e.ease || E)(clamp((t - e.at) / d));
74
+ let to;
75
+ if (kind === 'pan' || kind === 'glide') to = { x: e.x ?? cam.x, y: e.y ?? cam.y, z: cam.z };
76
+ else if (kind === 'to') to = { x: e.x ?? cam.x, y: e.y ?? cam.y, z: e.z ?? cam.z };
77
+ else if (kind === 'pull') to = { x: e.x ?? cam.x, y: e.y ?? cam.y, z: e.z ?? cam.z * (1 + (e.k ?? -1 / 6)) }; // 默认 ×5/6,正好抵消一次默认缓推(×1.2)
78
+ else to = { x: e.x ?? cam.x, y: e.y ?? cam.y, z: cam.z * (1 + (e.k ?? (kind === 'push' ? 0.2 : P.k))) };
79
+ if (p < 1) moving = true;
80
+ cam = { x: lerp(cam.x, to.x, p), y: lerp(cam.y, to.y, p), z: CAM.zlerp(cam.z, to.z, p) };
81
+ }
82
+ return { x: cam.x, y: cam.y + dy, z: cam.z * zMul, moving };
83
+ };
84
+
85
+ // ---------- 视差 ----------
86
+ // d=1 是主体平面,d<1 远、d>1 近。远层位移按 d 线性、缩放按 z^d:深推时远景几乎不动,这就是景深感(Kurzgesagt 5 层视差)。
87
+ CAM.layer = (c, cam, d, fn) => { const k = n(cam); c.save(); CAM.apply(c, { x: lerp(W / 2, k.x, d), y: lerp(H / 2, k.y, d), z: Math.pow(k.z, d) }); fn(c); c.restore(); };
88
+
89
+ // ---------- 运动 ----------
90
+ // 相机在屏幕上的瞬时速度(像素/帧,60fps):camFn(t) → cam。给 motionBlur 用。
91
+ CAM.velocity = (camFn, t, dt = 1 / 60) => { const a = camFn(t), b = camFn(t - dt); const z = n(a).z; return [(n(a).x - n(b).x) * z, (n(a).y - n(b).y) * z]; };
92
+ // 动态模糊:沿速度方向把整帧叠 n 次(快摇/甩镜用;速度 <1.5px 自动退化成一次绘制)。模糊长度应短于相机位移(PremiumBeat)。
93
+ CAM.motionBlur = (c, src, vx, vy, k = 7) => {
94
+ const sp = Math.hypot(vx, vy); if (sp < 1.5) { c.drawImage(src, 0, 0); return; }
95
+ c.save(); for (let i = 0; i < k; i++) { const q = i / (k - 1) - 0.5; c.globalAlpha = i === 0 ? 1 : 1 / (i + 1); c.drawImage(src, -vx * q, -vy * q); } c.restore();
96
+ };
97
+ // 手持感微漂:两路不同周期的正弦(连续时间,不闪),返回 [dx, dy] 像素。只给艺术风格画面和纯氛围镜头;解说镜头停住时就停住,不加微漂
98
+ CAM.drift = (t, amp = 6, seed = 0) => [amp * Math.sin(t * 0.9 + seed) + amp * 0.4 * Math.sin(t * 2.3 + seed * 2), amp * 0.7 * Math.sin(t * 1.1 + seed * 3)];
99
+ })();