@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,152 @@
1
+ // 数据图表动画 CH:比例尺、好看的刻度、网格(先出)、柱(从 0 长出)、折线(按时间画出、头部圆点)、K 线(逐根)、
2
+ // 标注(点→引线→文字)、经济学人式版式(顶红线+红色小旗+标题+单位副标题+来源注+「示意数据」角标)。
3
+ // 顺序铁律(准确性+可读性):坐标系先出 → 数据后出 → 标注最后、一镜只标一件事;柱状图基线必须是 0(纵轴不从 0 开始要在副标题写明);
4
+ // 动画只改「怎么出现」,不改数值;单位写在副标题;虚构/示意数据每一镜角落都标。
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 CH = window.CH = {};
9
+ const seg = (t, a, b) => clamp((t - a) / (b - a));
10
+
11
+ // 线性比例尺:值 → 屏幕;f.inv 反查
12
+ CH.lin = (d0, d1, r0, r1) => { const f = v => r0 + (v - d0) / (d1 - d0) * (r1 - r0); f.inv = y => d0 + (y - r0) / (r1 - r0) * (d1 - d0); return f; };
13
+ // 1/2/2.5/5×10^k 的好看刻度
14
+ CH.ticks = (min, max, n = 5) => {
15
+ const raw = (max - min) / n, mag = Math.pow(10, Math.floor(Math.log10(raw))), steps = [1, 2, 2.5, 5, 10];
16
+ const step = steps.map(s => s * mag).find(s => s >= raw) || 10 * mag;
17
+ const out = []; for (let v = Math.ceil(min / step) * step; v <= max + 1e-9; v += step) out.push(+v.toFixed(10)); return out;
18
+ };
19
+ CH.mixHex = (a, b, t) => { const A = PAINT.hex(a), B = PAINT.hex(b); return `rgb(${lerp(A[0], B[0], t) | 0},${lerp(A[1], B[1], t) | 0},${lerp(A[2], B[2], t) | 0})`; };
20
+
21
+ // ---------- 网格:水平线从左往右逐条画出(lag 错开),零线更深更粗;刻度值跟着淡入 ----------
22
+ // F = {x,y,w,h} 绘图区;ys = 刻度值;yS = 值→屏幕 y;p = 0..1
23
+ CH.grid = (c, F, ys, yS, p, { col = 'rgba(0,0,0,.14)', zeroCol = '#121212', lw = 1.5, zeroLw = 3, lag = 0.12, font = '28px sans-serif', labelCol = '#5b5b5b', fmt = v => String(v), side = 'right', labelDy = -10 } = {}) => {
24
+ c.save(); c.font = font; c.textBaseline = 'alphabetic';
25
+ ys.forEach((v, i) => {
26
+ const q = MO.lagged(i, ys.length, p, lag), e = MO.quintOut(q); if (q <= 0) return;
27
+ const y = Math.round(yS(v)) + 0.5, isZero = Math.abs(v) < 1e-9;
28
+ c.strokeStyle = isZero ? zeroCol : col; c.lineWidth = isZero ? zeroLw : lw;
29
+ c.beginPath(); c.moveTo(F.x, y); c.lineTo(F.x + F.w * e, y); c.stroke();
30
+ c.globalAlpha = seg(q, 0.3, 1); c.fillStyle = labelCol;
31
+ if (side === 'right') { c.textAlign = 'right'; c.fillText(fmt(v), F.x + F.w, y + labelDy); }
32
+ else { c.textAlign = 'left'; c.fillText(fmt(v), F.x, y + labelDy); }
33
+ c.globalAlpha = 1;
34
+ });
35
+ c.restore();
36
+ };
37
+ // x 轴标签(年份等):逐个淡入上移。items = [{x, label}]
38
+ CH.xLabels = (c, items, y, p, { font = '28px sans-serif', col = '#3a3a3a', lag = 0.1 } = {}) => {
39
+ c.save(); c.font = font; c.textAlign = 'center'; c.fillStyle = col;
40
+ items.forEach((it, i) => { const q = MO.quintOut(MO.lagged(i, items.length, p, lag)); if (q <= 0) return; c.globalAlpha = q; c.fillText(it.label, it.x, y + (1 - q) * 14); });
41
+ c.restore();
42
+ };
43
+
44
+ // ---------- 柱:从基线 0 长出(quintOut),数值标签与柱顶同步计数(等宽数字) ----------
45
+ // data: [{x, v}];bw 柱宽;p 0..1 总进度;hi 高亮下标(其余按 hiP 过渡到 dimCol)。返回各柱顶 [[x,y]]
46
+ CH.bars = (c, data, yS, bw, p, { lag = 0.18, col = '#1f5c99', hi = -1, hiP = 0, hiCol, dimCol = '#b9c3cc', label = true, fmt = v => v.toFixed(0), font = '30px sans-serif', labelCol = '#121212' } = {}) => {
47
+ const y0 = yS(0), tops = [];
48
+ data.forEach((d, i) => {
49
+ const q = MO.lagged(i, data.length, p, lag), e = MO.quintOut(q);
50
+ const v = d.v * e, y = yS(v);
51
+ let fill = col;
52
+ if (hi >= 0) fill = i === hi ? (hiCol || col) : CH.mixHex(col, dimCol, hiP);
53
+ c.fillStyle = fill; c.fillRect(d.x - bw / 2, y, bw, y0 - y);
54
+ tops.push([d.x, y]);
55
+ if (label && q > 0) {
56
+ c.save(); c.font = font; c.textAlign = 'center'; c.fillStyle = labelCol; c.globalAlpha = seg(q, 0.05, 0.4);
57
+ if (d.v < 0) { c.textBaseline = 'top'; TY.tabular(c, fmt(v), d.x, y + 12, { align: 'center' }); } // 负数柱:数值写在柱底下方,别写进柱子里
58
+ else TY.tabular(c, fmt(v), d.x, y - 14, { align: 'center' });
59
+ c.restore();
60
+ }
61
+ });
62
+ return tops;
63
+ };
64
+
65
+ // ---------- 折线:按 x(时间)推进画出;头部圆点;可选面积渐变;返回头部点 ----------
66
+ // pts: 屏幕坐标(x 递增);p: 0..1 = 画到 x 范围的哪里。area = {top, base, c0, c1}
67
+ CH.line = (c, pts, p, { col = '#e3120b', lw = 5, head = true, headR = 9, area, glow } = {}) => {
68
+ if (p <= 0) return null;
69
+ const xEnd = lerp(pts[0][0], pts[pts.length - 1][0], clamp(p));
70
+ const vis = [pts[0]];
71
+ for (let i = 1; i < pts.length; i++) {
72
+ if (pts[i][0] <= xEnd) vis.push(pts[i]);
73
+ else { const a = pts[i - 1], b = pts[i], q = (xEnd - a[0]) / (b[0] - a[0]); vis.push([xEnd, lerp(a[1], b[1], q)]); break; }
74
+ }
75
+ const hp = vis[vis.length - 1];
76
+ if (area) {
77
+ const g = c.createLinearGradient(0, area.top, 0, area.base); g.addColorStop(0, area.c0); g.addColorStop(1, area.c1);
78
+ c.save(); c.beginPath(); c.moveTo(vis[0][0], area.base); vis.forEach(q => c.lineTo(q[0], q[1])); c.lineTo(hp[0], area.base); c.closePath(); c.fillStyle = g; c.fill(); c.restore();
79
+ }
80
+ c.save(); c.strokeStyle = col; c.lineWidth = lw; c.lineJoin = 'round'; c.lineCap = 'round';
81
+ if (glow) { c.shadowColor = col; c.shadowBlur = glow; }
82
+ c.beginPath(); vis.forEach((q, i) => i ? c.lineTo(q[0], q[1]) : c.moveTo(q[0], q[1])); c.stroke(); c.restore();
83
+ if (head && p < 1.0001) { c.save(); c.fillStyle = col; c.beginPath(); c.arc(hp[0], hp[1], headR, 0, Math.PI * 2); c.fill();
84
+ c.strokeStyle = '#fff'; c.lineWidth = 3; c.stroke(); c.restore(); }
85
+ return hp;
86
+ };
87
+ // 折线在 x 处的 y(标注要钉在线上)
88
+ CH.yAt = (pts, x) => { for (let i = 1; i < pts.length; i++) if (pts[i][0] >= x) { const a = pts[i - 1], b = pts[i]; return lerp(a[1], b[1], (x - a[0]) / (b[0] - a[0])); } return pts[pts.length - 1][1]; };
89
+
90
+ // ---------- K 线:逐根出现,实体从开盘价长到收盘价,影线随后;默认红涨绿跌(A 股惯例,海外片换成绿涨红跌并在副标题写明) ----------
91
+ // rows: [{o,h,l,c}];xs: 每根中心 x;cw 实体宽;p 0..1
92
+ CH.candles = (c, rows, xs, yS, cw, p, { up = '#e0322f', down = '#11a05a', lag = 0.06, hollowUp = false } = {}) => {
93
+ rows.forEach((r, i) => {
94
+ const q = MO.lagged(i, rows.length, p, lag); if (q <= 0) return;
95
+ const e = MO.quintOut(seg(q, 0, 0.7)), w = MO.quintOut(seg(q, 0.35, 1));
96
+ const isUp = r.c >= r.o, col = isUp ? up : down, x = xs[i];
97
+ const yo = yS(r.o), yc = lerp(yo, yS(r.c), e);
98
+ c.save(); c.strokeStyle = col; c.fillStyle = col; c.lineWidth = 2.5;
99
+ if (w > 0) { const ym = (yo + yS(r.c)) / 2; c.beginPath(); c.moveTo(x, lerp(ym, yS(r.h), w)); c.lineTo(x, lerp(ym, yS(r.l), w)); c.stroke(); }
100
+ const top = Math.min(yo, yc), hgt = Math.max(2, Math.abs(yc - yo));
101
+ if (isUp && hollowUp) { c.fillStyle = '#fff'; c.fillRect(x - cw / 2, top, cw, hgt); c.strokeRect(x - cw / 2, top, cw, hgt); }
102
+ else c.fillRect(x - cw / 2, top, cw, hgt);
103
+ c.restore();
104
+ });
105
+ };
106
+
107
+ // ---------- 标注:圆点弹出(弹簧)→ 脉冲环 → 引线画出 → 文字淡入。时间用秒,便于对口播 ----------
108
+ // lt 秒;t0 开始时刻;(x,y) 数据点;(tx,ty) 文字锚点;lines 第一行用 font、其余用 font2(数字先说,比较后说)
109
+ CH.callout = (c, lt, t0, { x, y, tx, ty, lines = [], col = '#121212', dotCol = '#e3120b', font = '600 30px sans-serif', font2, align = 'left', r = 11 }) => {
110
+ const k = lt - t0; if (k <= 0) return;
111
+ const s = MO.spring(k, { duration: 0.45, bounce: 0.25 });
112
+ c.save();
113
+ c.fillStyle = dotCol; c.beginPath(); c.arc(x, y, r * Math.max(0, s), 0, Math.PI * 2); c.fill();
114
+ const pr = seg(k, 0, 0.6); if (pr > 0 && pr < 1) { c.strokeStyle = dotCol; c.globalAlpha = 1 - pr; c.lineWidth = 3; c.beginPath(); c.arc(x, y, r + pr * 34, 0, Math.PI * 2); c.stroke(); c.globalAlpha = 1; }
115
+ const lp = MO.quintOut(seg(k, 0.12, 0.45));
116
+ if (lp > 0) { c.strokeStyle = col; c.lineWidth = 2; c.beginPath(); c.moveTo(x, y); c.lineTo(lerp(x, tx, lp), lerp(y, ty, lp)); c.stroke(); }
117
+ const fp = MO.quintOut(seg(k, 0.3, 0.7));
118
+ if (fp > 0) {
119
+ c.globalAlpha = fp; c.fillStyle = col; c.textAlign = align; c.textBaseline = 'alphabetic';
120
+ const dx = align === 'left' ? 12 : align === 'right' ? -12 : 0;
121
+ lines.forEach((L, i) => { c.font = i === 0 ? font : (font2 || font); c.fillText(L, tx + dx + (1 - fp) * (align === 'right' ? 16 : -16), ty + 8 + i * 40); });
122
+ }
123
+ c.restore();
124
+ };
125
+
126
+ // ---------- 版式 ----------
127
+ CH.tag = (c, x, y, w = 64, h = 14, col = '#e3120b') => { c.fillStyle = col; c.fillRect(x, y, w, h); };
128
+ // 「示意数据」角标:右下圆角框。虚构数据每一镜都要有
129
+ CH.demoBadge = (c, text = '示意数据', { x = W - 40, y = 1040, font = '600 24px sans-serif', col = '#7a7a7a', box = 'rgba(0,0,0,.06)' } = {}) => {
130
+ c.save(); c.font = font; c.textAlign = 'right'; c.textBaseline = 'middle';
131
+ const w = c.measureText(text).width + 28; c.fillStyle = box; c.beginPath(); c.roundRect(x - w, y - 22, w, 44, 8); c.fill();
132
+ c.strokeStyle = col; c.globalAlpha = 0.5; c.lineWidth = 1.5; c.stroke(); c.globalAlpha = 1;
133
+ c.fillStyle = col; c.fillText(text, x - 14, y + 1); c.restore();
134
+ };
135
+ // 经济学人式整页版式(每镜都画):白底 → 顶部细红线从左画出 → 左上红色小旗 → 标题 → 副标题(单位/口径)→ 左下来源 → 右下「示意数据」。
136
+ // lt = 本镜时间(版式 0.6s 内全部到位,之后才出坐标系)。badge 传 false 不画角标(真实数据时)。
137
+ CH.frame = (c, lt, { title, sub, source = '来源:示意数据', badge = '示意数据', bg = '#FFFFFF', red = '#E3120B', ink = '#0C0C0C', subCol = '#4F5B61', srcCol = '#7c868b',
138
+ font = '"NotoSansSC"', x = 90 } = {}) => {
139
+ c.fillStyle = bg; c.fillRect(0, 0, W, H);
140
+ const p = MO.quintOut(MO.seg(lt, 0, 0.45));
141
+ c.fillStyle = red; c.fillRect(0, 0, W * p, 8);
142
+ CH.tag(c, x, 56, 96 * p, 22, red);
143
+ c.save(); c.textBaseline = 'alphabetic'; c.textAlign = 'left';
144
+ const tp = MO.quintOut(MO.seg(lt, 0.05, 0.5));
145
+ c.globalAlpha = tp; c.font = `700 58px ${font}`; c.fillStyle = ink; c.fillText(title, x, 150 + (1 - tp) * 18);
146
+ const sp = MO.quintOut(MO.seg(lt, 0.15, 0.6));
147
+ c.globalAlpha = sp; c.font = `400 34px ${font}`; c.fillStyle = subCol; c.fillText(sub, x, 205 + (1 - sp) * 14);
148
+ c.globalAlpha = 1; c.font = `400 26px ${font}`; c.fillStyle = srcCol; c.fillText(source, x, 1040);
149
+ c.restore();
150
+ if (badge) CH.demoBadge(c, badge, { x: W - 60, y: 1030, font: `700 28px ${font}`, col: red, box: 'rgba(227,18,11,0.06)' });
151
+ };
152
+ })();
@@ -0,0 +1,97 @@
1
+ // 拼贴材料 CL(Vox / Missing Chapter / Borders 一路):纸纹、撕纸边、网点照片、胶带、图钉、红线(下垂的线索绳)、
2
+ // 毛边荧光笔(multiply)、轻投影、带倾斜滑入放平、剪纸贴纸白边。
3
+ // 节奏铁律(y2 调研):相机 60fps 平滑;桌上的纸片/荧光笔/红笔/打字按 12fps「一拍二」步进(MO.step)——stop-motion 手感的一半。
4
+ // 纸纹是贴死的(静态颗粒,不逐帧抖)。素材一次画好走 PAINT.cached,确定性。
5
+ (() => {
6
+ let W = 1920, H = 1080; U.onStage((w, h) => { W = w; H = h; }); // 画布尺寸跟 U.setStage 走(默认 1920×1080)
7
+ const { clamp, lerp, rng } = U;
8
+ const CL = window.CL = {};
9
+
10
+ // ---------- 纸 ----------
11
+ // 可平铺纸纹 tile:环面取样的 fbm 明暗(无缝)+纤维短线+细颗粒。用 c.createPattern(tile, 'repeat') 铺
12
+ CL.paperTile = (key, base, { size = 512, amt = 10, fibers = 260, grain = 10, fiberCol = [120, 100, 70], seed = 3 } = {}) => PAINT.cached('ytpaper_' + key, size, size, (g) => {
13
+ const img = g.createImageData(size, size), d = img.data, b = PAINT.hex(base), r = rng(seed);
14
+ for (let y = 0; y < size; y++) for (let x = 0; x < size; x++) {
15
+ const ax = x / size * Math.PI * 2, ay = y / size * Math.PI * 2;
16
+ const n = PAINT.fbm(Math.cos(ax) * 1.6 + 10, Math.sin(ax) * 1.6 + Math.cos(ay) * 1.6, 4) * 0.6 + PAINT.noise(Math.sin(ay) * 3 + 5, Math.cos(ay) * 3) * 0.4;
17
+ const gr = (r() - .5) * grain, i = (y * size + x) * 4;
18
+ d[i] = b[0] + n * amt + gr; d[i + 1] = b[1] + n * amt + gr; d[i + 2] = b[2] + n * amt * 0.9 + gr; d[i + 3] = 255;
19
+ }
20
+ g.putImageData(img, 0, 0);
21
+ g.lineCap = 'round';
22
+ for (let k = 0; k < fibers; k++) { const x = r() * size, y = r() * size, a = r() * Math.PI, l = 4 + r() * 14;
23
+ g.strokeStyle = PAINT.rgb(fiberCol, 0.05 + r() * 0.08); g.lineWidth = 0.6 + r() * 0.8;
24
+ for (const ox of [0, -size, size]) for (const oy of [0, -size, size]) { g.beginPath(); g.moveTo(x + ox, y + oy); g.quadraticCurveTo(x + ox + Math.cos(a + 1) * l * 0.5, y + oy + Math.sin(a + 1) * l * 0.5, x + ox + Math.cos(a) * l, y + oy + Math.sin(a) * l); g.stroke(); } }
25
+ });
26
+ CL.fillPaper = (c, tile, x, y, w, h) => { c.save(); c.fillStyle = c.createPattern(tile, 'repeat'); c.fillRect(x, y, w, h); c.restore(); };
27
+ // 撕纸边:沿一条线生成锯齿+纤维的点列(左→右),amp 起伏、seed 固定
28
+ CL.tornEdge = (x0, y0, x1, y1, { step = 9, amp = 10, seed = 5 } = {}) => {
29
+ const r = rng(seed), L = Math.hypot(x1 - x0, y1 - y0), n = Math.max(2, Math.round(L / step)), nx = -(y1 - y0) / L, ny = (x1 - x0) / L, o = [];
30
+ for (let i = 0; i <= n; i++) { const q = i / n, k = PAINT.noise(i * 0.18, seed) * amp + (r() - .5) * amp * 0.9; o.push([lerp(x0, x1, q) + nx * k, lerp(y0, y1, q) + ny * k]); }
31
+ return o;
32
+ };
33
+ // 撕边矩形 (0,0,w,h):只建路径(beginPath…closePath),调用方 fill / clip
34
+ CL.tornRect = (g, w, h, seed, amp = 5) => {
35
+ const pts = [...CL.tornEdge(0, 0, w, 0, { amp, seed }), ...CL.tornEdge(w, 0, w, h, { amp, seed: seed + 1 }), ...CL.tornEdge(w, h, 0, h, { amp, seed: seed + 2 }), ...CL.tornEdge(0, h, 0, 0, { amp, seed: seed + 3 })];
36
+ g.beginPath(); pts.forEach((p, i) => i ? g.lineTo(p[0], p[1]) : g.moveTo(p[0], p[1])); g.closePath();
37
+ };
38
+
39
+ // ---------- 网点照片 ----------
40
+ // 灰度网点:源图(canvas)按亮度转成 45° 网点,cell 像素一格;只留在源图不透明处。结果缓存(key 唯一)
41
+ CL.halftone = (key, src, { cell = 7, ink = '#1b1b1b', paper = '#efe9dc', angle = Math.PI / 4, gamma = 1, contrast = 1.15 } = {}) => PAINT.cached('ytht_' + key, src.width, src.height, (g) => {
42
+ const w = src.width, h = src.height, sd = src.getContext('2d').getImageData(0, 0, w, h).data;
43
+ g.fillStyle = paper; g.fillRect(0, 0, w, h); g.fillStyle = ink;
44
+ const ca = Math.cos(angle), sa = Math.sin(angle), R = Math.hypot(w, h);
45
+ g.beginPath();
46
+ for (let v = -R; v < R; v += cell) for (let u = -R; u < R; u += cell) {
47
+ const x = w / 2 + u * ca - v * sa, y = h / 2 + u * sa + v * ca; if (x < -cell || y < -cell || x > w + cell || y > h + cell) continue;
48
+ const ix = clamp(x | 0, 0, w - 1), iy = clamp(y | 0, 0, h - 1), i = (iy * w + ix) * 4;
49
+ if (sd[i + 3] < 10) continue;
50
+ let L = (sd[i] * .3 + sd[i + 1] * .59 + sd[i + 2] * .11) / 255; L = clamp((L - 0.5) * contrast + 0.5); L = Math.pow(L, gamma);
51
+ const rr = cell * 0.62 * Math.sqrt(1 - L); if (rr < 0.4) continue;
52
+ g.moveTo(x + rr, y); g.arc(x, y, rr, 0, Math.PI * 2);
53
+ }
54
+ g.fill();
55
+ g.globalCompositeOperation = 'destination-in'; g.drawImage(src, 0, 0); g.globalCompositeOperation = 'source-over';
56
+ });
57
+ // 网点照片贴纸:draw(g, w, h) 画源图 → 网点 → 加 border 宽的纸边、一层旧纸色(multiply)。返回缓存 canvas(尺寸 w+2b × h+2b)
58
+ CL.photo = (key, w, h, draw, { border = 14, cell = 6, paper = '#f2f0ea', ink = '#1a1a1a', htPaper = '#e8e4da', contrast = 1.3 } = {}) => PAINT.cached('clphoto_' + key, w + border * 2, h + border * 2, (g) => {
59
+ const src = PAINT.canvas(w, h); draw(src.getContext('2d'), w, h);
60
+ const ht = CL.halftone(key + '_ht', src, { cell, ink, paper: htPaper, contrast });
61
+ g.fillStyle = paper; g.fillRect(0, 0, w + border * 2, h + border * 2); g.drawImage(ht, border, border);
62
+ g.globalCompositeOperation = 'multiply'; g.fillStyle = 'rgba(214,205,185,.3)'; g.fillRect(0, 0, w + border * 2, h + border * 2); g.globalCompositeOperation = 'source-over';
63
+ });
64
+
65
+ // ---------- 摆放 ----------
66
+ CL.placed = (g, x, y, r, fn) => { g.save(); g.translate(x, y); g.rotate(r); fn(g); g.restore(); };
67
+ // 轻投影(Vox 桌面上留一点就够;黑底卡片几乎无投影)
68
+ CL.shadowed = (g, fn, { col = 'rgba(50,40,25,.28)', blur = 10, x = 3, y = 6 } = {}) => { g.save(); g.shadowColor = col; g.shadowBlur = blur; g.shadowOffsetX = x; g.shadowOffsetY = y; fn(); g.restore(); };
69
+ // 带倾斜滑入、到位放平(MC 实测:滑入带 5–15° 倾斜,约 0.6–0.8s),按 12fps 步进。返回进度 q
70
+ CL.slideIn = (g, t, t0, x, y, r, fn, { from = [420, -60], tilt = 0.2, dur = 0.6, fps = 12 } = {}) => {
71
+ const q = clamp((MO.step(t, fps) - t0) / dur); if (q <= 0) return 0;
72
+ const e = MO.quartOut(q);
73
+ g.save(); g.translate(x + from[0] * (1 - e), y + from[1] * (1 - e)); g.rotate(r + tilt * (1 - Math.min(1, e * 1.15))); fn(g); g.restore(); return q;
74
+ };
75
+ // 胶带:一条半透明撕边纸条
76
+ CL.tape = (g, x, y, a, w = 120, col = 'rgba(226,220,200,.85)') => { g.save(); g.translate(x, y); g.rotate(a); g.fillStyle = col; g.translate(-w / 2, -17); CL.tornRect(g, w, 34, (x * 7 + y) | 0, 3); g.fill(); g.restore(); };
77
+ // 图钉
78
+ CL.pin = (g, x, y, col = '#b8433f') => { g.save(); g.translate(x, y); g.fillStyle = 'rgba(0,0,0,.25)'; g.beginPath(); g.arc(3, 5, 12, 0, 7); g.fill(); g.fillStyle = col; g.beginPath(); g.arc(0, 0, 12, 0, 7); g.fill(); g.fillStyle = 'rgba(255,255,255,.55)'; g.beginPath(); g.arc(-4, -4, 4, 0, 7); g.fill(); g.restore(); };
79
+ // 调查墙的线索绳:a→b 之间按长度 sag 下垂的二次曲线点列(配 DG.cum / DG.drawPartial 逐段描出)
80
+ CL.stringPts = (a, b, sag = 0.08, n = 48) => { const mx = (a[0] + b[0]) / 2, my = (a[1] + b[1]) / 2 + Math.hypot(b[0] - a[0], b[1] - a[1]) * sag; const o = []; for (let i = 0; i <= n; i++) { const t = i / n, u = 1 - t; o.push([u * u * a[0] + 2 * u * t * mx + t * t * b[0], u * u * a[1] + 2 * u * t * my + t * t * b[1]]); } return o; };
81
+ // 红线:带投影描出前 d 长度(d 用弧长;整根画完传 1e9)
82
+ CL.string = (g, pts, cum, d, { col = '#b8433f', lw = 5, shadow = 'rgba(0,0,0,.16)' } = {}) => {
83
+ if (shadow) { g.save(); g.strokeStyle = shadow; g.lineWidth = lw; g.translate(3, 6); DG.drawPartial(g, pts, cum, d); g.restore(); }
84
+ g.save(); g.strokeStyle = col; g.lineWidth = lw; g.lineCap = 'round'; DG.drawPartial(g, pts, cum, d); g.restore();
85
+ };
86
+ // 毛边荧光笔(multiply,压在印刷字上):q 0..1 从左扫到右
87
+ CL.highlight = (g, x0, y0, w, h, q, col = 'rgba(218,207,8,.9)') => { if (q <= 0) return; g.save(); g.globalCompositeOperation = 'multiply'; g.fillStyle = col;
88
+ g.beginPath(); const ww = w * q; g.moveTo(x0, y0 + 4); for (let i = 0; i <= 20; i++) g.lineTo(x0 + ww * i / 20, y0 + Math.sin(i * 1.7) * 2); g.lineTo(x0 + ww + 6, y0 + h / 2); g.lineTo(x0 + ww, y0 + h);
89
+ for (let i = 20; i >= 0; i--) g.lineTo(x0 + ww * i / 20, y0 + h + Math.sin(i * 2.3) * 2); g.closePath(); g.fill(); g.restore(); };
90
+ // 剪纸贴纸白边:把 src(全屏离屏里的角色/物件)向四周膨胀 border 像素铺 col,返回同尺寸离屏;用法:先画返回值、再画 src
91
+ CL.stickerEdge = (src, key, border = 12, col = '#f4f2ec', dirs = 16) => {
92
+ const sil = PAINT.scratch(key), sg = sil.getContext('2d'); sg.reset();
93
+ for (let a = 0; a < dirs; a++) sg.drawImage(src, Math.cos(a / dirs * 6.283) * border, Math.sin(a / dirs * 6.283) * border);
94
+ sg.globalCompositeOperation = 'source-in'; sg.fillStyle = col; sg.fillRect(0, 0, W, H);
95
+ return sil;
96
+ };
97
+ })();
@@ -0,0 +1,264 @@
1
+ // 图解与形变 DG:点列、逐笔描出(draw-on)、揭开遮罩、手绘抖动线、manim 式 Transform / Write、白板笔画时间线+跟随的笔、
2
+ // 神经网络(神经元/连线/信号闪过)、Indicate / Circumscribe / 大括号、3b1b 色板。
3
+ // 约定:点列 pts = [[x,y],...];cum = DG.cum(pts) 是累计弧长;「画到 d」都按弧长算。确定性:随机全走种子。
4
+ (() => {
5
+ let W = 1920, H = 1080; U.onStage((w, h) => { W = w; H = h; }); // 画布尺寸跟 U.setStage 走(默认 1920×1080)
6
+ const { clamp, lerp, rng } = U;
7
+ const DG = window.DG = {};
8
+
9
+ // ---------- 点列 ----------
10
+ DG.cum = pts => { const L = [0]; for (let i = 1; i < pts.length; i++) L.push(L[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); return L; };
11
+ DG.len = pts => { const L = DG.cum(pts); return L[L.length - 1]; };
12
+ // 折线上弧长 d 处的点
13
+ DG.pointAt = (pts, cum, d) => {
14
+ if (d <= 0) return pts[0]; const n = pts.length; if (d >= cum[n - 1]) return pts[n - 1];
15
+ let lo = 0, hi = n - 1; while (hi - lo > 1) { const m = (lo + hi) >> 1; if (cum[m] < d) lo = m; else hi = m; }
16
+ const q = (d - cum[lo]) / (cum[hi] - cum[lo] || 1); return [lerp(pts[lo][0], pts[hi][0], q), lerp(pts[lo][1], pts[hi][1], q)];
17
+ };
18
+ // 逐笔描出:画折线的前 d 长度(用当前 strokeStyle/lineWidth),返回笔尖位置。白板、红笔圈、红线连接都用它。
19
+ DG.drawPartial = (c, pts, cum, d) => {
20
+ const n = pts.length; if (d <= 0 || n < 2) return pts[0];
21
+ c.beginPath(); c.moveTo(pts[0][0], pts[0][1]);
22
+ let i = 1; for (; i < n && cum[i] <= d; i++) c.lineTo(pts[i][0], pts[i][1]);
23
+ let tip = pts[n - 1];
24
+ if (i < n) { tip = DG.pointAt(pts, cum, d); c.lineTo(tip[0], tip[1]); }
25
+ c.stroke(); return tip;
26
+ };
27
+ // 变换点列:平移 (dx,dy)、缩放 s
28
+ DG.xform = (pts, dx, dy, s = 1) => pts.map(([x, y]) => [dx + x * s, dy + y * s]);
29
+ // 椭圆弧点列(a0→a1)
30
+ DG.arcPts = (cx, cy, rx, ry, a0, a1, n = 24) => { const o = []; for (let i = 0; i <= n; i++) { const a = a0 + (a1 - a0) * i / n; o.push([cx + Math.cos(a) * rx, cy + Math.sin(a) * ry]); } return o; };
31
+ // 手画的圈:从 a0 起转 turns 圈(>1 首尾略交叉,像真的用笔圈)
32
+ DG.ellipsePts = (cx, cy, rx, ry, a0 = -Math.PI / 2, turns = 1.08, n = 64) => DG.arcPts(cx, cy, rx, ry, a0, a0 + turns * Math.PI * 2, n);
33
+ // 手绘抖动:Catmull-Rom 加密后按种子加低频抖动(静态,不沸腾——白板上的线画完就不动)
34
+ DG.hand = (pts, { per = 8, amp = 1.6, seed = 1, freq = 0.035 } = {}) => {
35
+ const d = KIT.densify(pts, per), r = rng(seed), ph = r() * 100;
36
+ return KIT.resample(d, 3).map(([x, y], i) => [x + PAINT.noise(i * freq + ph, 3.1) * amp * 2, y + PAINT.noise(i * freq + ph, 7.7) * amp * 2]);
37
+ };
38
+ // 揭开遮罩:沿折线前 d 长度,每段做成同向绕行的方条(方头延长 w/2),合成一条 Path2D 用来 clip。
39
+ // VideoScribe 的「Reveal」画法:之字形笔触从上往下揭开一张画;也用于逐字书写。同向绕行保证 nonzero 下重叠处不抵消。
40
+ DG.revealMask = (pts, cum, d, w) => {
41
+ const p = new Path2D(), h = w / 2;
42
+ for (let i = 1; i < pts.length && cum[i - 1] < d; i++) {
43
+ const a = pts[i - 1], b0 = pts[i], L = cum[i] - cum[i - 1]; if (L <= 0) continue;
44
+ const q = Math.min(1, (d - cum[i - 1]) / L), b = [a[0] + (b0[0] - a[0]) * q, a[1] + (b0[1] - a[1]) * q];
45
+ const ux = (b0[0] - a[0]) / L, uy = (b0[1] - a[1]) / L, nx = -uy * h, ny = ux * h;
46
+ const A = [a[0] - ux * h, a[1] - uy * h], B = [b[0] + ux * h, b[1] + uy * h];
47
+ p.moveTo(A[0] + nx, A[1] + ny); p.lineTo(B[0] + nx, B[1] + ny); p.lineTo(B[0] - nx, B[1] - ny); p.lineTo(A[0] - nx, A[1] - ny); p.closePath();
48
+ }
49
+ return p;
50
+ };
51
+ // 之字形揭开路径:在框 (x,y,w,h) 里从上往下 rows 行来回
52
+ DG.zigzag = (x, y, w, h, rows) => { const o = []; for (let r = 0; r <= rows; r++) { const yy = y + h * r / rows; o.push(r % 2 ? [x + w, yy] : [x, yy]); } return o; };
53
+
54
+ // ---------- manim Transform:两边点数相同、起点对齐(都从左上方向起、顺时针)才能逐点插值 ----------
55
+ DG.rectPts = (cx, cy, w, h, n = 64) => {
56
+ const per = 2 * (w + h), out = [];
57
+ for (let i = 0; i < n; i++) {
58
+ let d = i / n * per; let x, y;
59
+ if (d < w) { x = cx - w / 2 + d; y = cy - h / 2; }
60
+ else if ((d -= w) < h) { x = cx + w / 2; y = cy - h / 2 + d; }
61
+ else if ((d -= h) < w) { x = cx + w / 2 - d; y = cy + h / 2; }
62
+ else { d -= w; x = cx - w / 2; y = cy + h / 2 - d; }
63
+ out.push([x, y]);
64
+ }
65
+ return out;
66
+ };
67
+ DG.circlePts = (cx, cy, r, n = 64) => { const out = []; for (let i = 0; i < n; i++) { const a = -3 * Math.PI / 4 + i / n * 2 * Math.PI; out.push([cx + Math.cos(a) * r, cy + Math.sin(a) * r]); } return out; };
68
+ DG.morph = (A, B, p) => A.map((a, i) => [lerp(a[0], B[i][0], p), lerp(a[1], B[i][1], p)]);
69
+ DG.shape = (c, pts, { fill, stroke, lw = 2 } = {}) => {
70
+ c.beginPath(); pts.forEach((q, i) => i ? c.lineTo(q[0], q[1]) : c.moveTo(q[0], q[1])); c.closePath();
71
+ if (fill) { c.fillStyle = fill; c.fill(); }
72
+ if (stroke && lw > 0) { c.strokeStyle = stroke; c.lineWidth = lw; c.lineJoin = 'round'; c.stroke(); }
73
+ };
74
+
75
+ // ---------- 颜色(接受 '#hex' 或 [r,g,b]) ----------
76
+ DG.arr = h => Array.isArray(h) ? h : PAINT.hex(h);
77
+ DG.mixA = (h1, h2, t) => { const a = DG.arr(h1), b = DG.arr(h2); return [lerp(a[0], b[0], t), lerp(a[1], b[1], t), lerp(a[2], b[2], t)]; };
78
+ DG.rgba = (h, a = 1) => { const v = DG.arr(h); return `rgba(${v[0] | 0},${v[1] | 0},${v[2] | 0},${a})`; };
79
+ DG.mix = (h1, h2, t) => DG.rgba(DG.mixA(h1, h2, t));
80
+ // 3b1b 色板。Grant 的 custom_config 在 ffmpeg 导出时 saturation=1.5,照 manim 原色值直接用会发灰,所以 DG.MANIM 是饱和后的。
81
+ DG.MANIM_RAW = { BLUE_C: '#58C4DD', BLUE_D: '#29ABCA', BLUE_E: '#1C758A', TEAL_C: '#5CD0B3', GREEN_C: '#83C167', YELLOW: '#FFFF00', GOLD_C: '#F0AC5F',
82
+ RED_C: '#FC6255', MAROON_C: '#C55F73', PURPLE_C: '#9A72AC', GREY_A: '#DDDDDD', GREY_B: '#BBBBBB', GREY_C: '#888888', GREY_D: '#444444', GREY_E: '#222222', WHITE: '#FFFFFF', BLACK: '#000000' };
83
+ DG.saturate = (hex, s = 1.5) => { // CSS/SVG saturate 矩阵
84
+ const [r, g, b] = PAINT.hex(hex);
85
+ const R = (0.213 + 0.787 * s) * r + (0.715 - 0.715 * s) * g + (0.072 - 0.072 * s) * b;
86
+ const G = (0.213 - 0.213 * s) * r + (0.715 + 0.285 * s) * g + (0.072 - 0.072 * s) * b;
87
+ const B = (0.213 - 0.213 * s) * r + (0.715 - 0.715 * s) * g + (0.072 + 0.928 * s) * b;
88
+ const f = v => Math.round(clamp(v, 0, 255)).toString(16).padStart(2, '0');
89
+ return '#' + f(R) + f(G) + f(B);
90
+ };
91
+ DG.MANIM = {}; for (const k in DG.MANIM_RAW) DG.MANIM[k] = DG.saturate(DG.MANIM_RAW[k]);
92
+ DG.U = H / 8; // manim:画面高 8 个单位 → 1 单位 = 135px(1080p)
93
+
94
+ // ---------- Write 与公式(CMU = LaTeX Computer Modern;中文用思源宋) ----------
95
+ DG.FONT = { rm: 'CMU-rm', it: 'CMU-it', zh: 'NotoSerifSC' };
96
+ const famOf = tk => tk.zh ? DG.FONT.zh : tk.it ? DG.FONT.it : DG.FONT.rm;
97
+ // tokens: [{s, it, zh, col, sub, sup, size, gap}];返回每个 token 的 {x,w,sz}(相对起点)
98
+ DG.layout = (c, tokens, size) => {
99
+ let x = 0; return tokens.map(tk => {
100
+ const sz = (tk.size || 1) * size; c.font = `${sz}px "${famOf(tk)}"`;
101
+ const w = c.measureText(tk.s).width; let sw = 0;
102
+ if (tk.sub || tk.sup) { c.font = `${sz * 0.62}px "${DG.FONT.rm}"`; sw = c.measureText(tk.sub || tk.sup).width + sz * 0.04; }
103
+ const r = { x, w: w + sw, sz }; x += w + sw + (tk.gap != null ? tk.gap : sz * 0.06); return r;
104
+ });
105
+ };
106
+ // manim Write:每个 token 先沿轮廓描边(lineDash 推进),后半程填色、描边淡掉;token 间 lag = min(4/(n+1), 0.2)(3b1b creation.py)。
107
+ // 尊重外层 globalAlpha(FadeOut 时整条公式一起淡)。返回每个 token 的屏幕 {x,w}。
108
+ DG.math = (c, tokens, x, y, size, { p = 1, align = 'center', col = '#fff', lag } = {}) => {
109
+ const L = DG.layout(c, tokens, size), total = L.length ? L[L.length - 1].x + L[L.length - 1].w : 0;
110
+ const x0 = align === 'center' ? x - total / 2 : align === 'right' ? x - total : x;
111
+ const n = tokens.length, r = lag != null ? lag : Math.min(4 / (n + 1), 0.2);
112
+ const A0 = c.globalAlpha;
113
+ c.save(); c.textBaseline = 'alphabetic'; c.textAlign = 'left';
114
+ tokens.forEach((tk, i) => {
115
+ const q = MO.lagged(i, n, clamp(p), r); if (q <= 0) return;
116
+ const sz = L[i].sz, fx = x0 + L[i].x, colr = tk.col || col;
117
+ const draw = (txt, xx, yy, f, mode) => { c.font = f; mode === 's' ? c.strokeText(txt, xx, yy) : c.fillText(txt, xx, yy); };
118
+ const main = `${sz}px "${famOf(tk)}"`, small = `${sz * 0.62}px "${DG.FONT.rm}"`;
119
+ c.font = main; const mw = c.measureText(tk.s).width;
120
+ const parts = [[tk.s, fx, y, main]]; if (tk.sub) parts.push([tk.sub, fx + mw + sz * 0.02, y + sz * 0.22, small]); if (tk.sup) parts.push([tk.sup, fx + mw + sz * 0.02, y - sz * 0.42, small]);
121
+ const qs = MO.doubleSmooth(q);
122
+ if (qs < 1) { c.strokeStyle = colr; c.lineWidth = Math.max(1.2, sz * 0.025); c.setLineDash([sz * 5 * clamp(qs * 2), 1e5]); c.globalAlpha = A0 * (1 - clamp((qs - 0.6) / 0.4)); parts.forEach(pt => draw(pt[0], pt[1], pt[2], pt[3], 's')); c.setLineDash([]); }
123
+ const fa = clamp((qs - 0.4) / 0.6); if (fa > 0) { c.globalAlpha = A0 * fa; c.fillStyle = colr; parts.forEach(pt => draw(pt[0], pt[1], pt[2], pt[3], 'f')); }
124
+ c.globalAlpha = A0;
125
+ });
126
+ c.restore();
127
+ return L.map(l => ({ x: x0 + l.x, w: l.w }));
128
+ };
129
+ // 纯文本的 Write(中文标题也走这个:非 ASCII 字自动用 DG.FONT.zh)
130
+ DG.write = (c, text, x, y, size, p, opt = {}) => DG.math(c, [...text].map(ch => ({ s: ch, zh: /[^\x00-\x7f]/.test(ch) || opt.zh, gap: 0 })), x, y, size, { ...opt, p });
131
+
132
+ // ---------- 神经网络 ----------
133
+ // 神经元:描边白 2px,填充不透明度 = 激活值(3b1b nn/part1.py 原做法)
134
+ DG.neuron = (c, x, y, r, a, { stroke = '#fff', fill = '#fff', lw = 2, sa = 1 } = {}) => {
135
+ c.save(); c.beginPath(); c.arc(x, y, r, 0, Math.PI * 2);
136
+ if (a > 0) { c.globalAlpha = clamp(a); c.fillStyle = fill; c.fill(); }
137
+ c.globalAlpha = sa; c.strokeStyle = stroke; c.lineWidth = lw; c.stroke(); c.restore();
138
+ };
139
+ // 圆周到圆周的连线端点
140
+ DG.edgeEnds = (ax, ay, ar, bx, by, br) => { const dx = bx - ax, dy = by - ay, d = Math.hypot(dx, dy) || 1; return [ax + dx / d * ar, ay + dy / d * ar, bx - dx / d * br, by - dy / d * br]; };
141
+ // 连线:正权重蓝、负权重红;线宽 = minW + maxW·|w|³(nn/part2.py:3·(|w|/max)³);p = 画到哪(ShowCreation)
142
+ DG.edge = (c, e, w, p = 1, { maxW = 3.2, minW = 0.6, pos = DG.MANIM.BLUE_C, neg = DG.MANIM.RED_C, alpha = 0.85, from = 0 } = {}) => {
143
+ if (p <= from) return;
144
+ const [x1, y1, x2, y2] = e;
145
+ c.save(); c.strokeStyle = w >= 0 ? pos : neg; c.globalAlpha = alpha; c.lineWidth = minW + maxW * Math.pow(Math.min(1, Math.abs(w)), 3);
146
+ c.beginPath(); c.moveTo(lerp(x1, x2, from), lerp(y1, y2, from)); c.lineTo(lerp(x1, x2, p), lerp(y1, y2, p)); c.stroke(); c.restore();
147
+ };
148
+ // ShowPassingFlash(time_width=2.0):信号沿线闪过,返回可见段 [lo, hi](0..1)
149
+ DG.flash = (alpha, tw = 2) => { const up = lerp(0, 1 + tw, alpha); return [clamp(up - tw), clamp(up)]; };
150
+
151
+ // ---------- 提示 ----------
152
+ DG.indicate = p => ({ s: 1 + 0.2 * MO.thereAndBack(clamp(p)), k: MO.thereAndBack(clamp(p)) }); // 放大 1.2、染黄(k)、回来
153
+ // Circumscribe:矩形框沿周长画出再收走(time_width 0.3 的近似)
154
+ DG.circumscribe = (c, x, y, w, h, p, { col = DG.MANIM.YELLOW, lw = 4, tw = 0.3 } = {}) => {
155
+ if (p <= 0 || p >= 1) return; const pts = DG.rectPts(x + w / 2, y + h / 2, w, h, 160);
156
+ const hi = clamp(lerp(0, 1 + tw, p)), lo = clamp(hi - tw - (1 - p) * 0.7);
157
+ c.save(); c.strokeStyle = col; c.lineWidth = lw; c.beginPath();
158
+ const i0 = Math.floor(lo * 160), i1 = Math.floor(hi * 160); for (let i = i0; i <= Math.min(160, i1); i++) { const q = pts[i % 160]; i === i0 ? c.moveTo(q[0], q[1]) : c.lineTo(q[0], q[1]); } c.stroke(); c.restore();
159
+ };
160
+ // 大括号(竖直,开口朝右),从中间向两端长出
161
+ DG.brace = (c, x, y0, y1, { col = '#fff', lw = 3, depth = 22, p = 1 } = {}) => {
162
+ if (p <= 0) return; const ym = (y0 + y1) / 2;
163
+ c.save(); c.strokeStyle = col; c.lineWidth = lw; c.lineCap = 'round'; c.globalAlpha = clamp(p * 1.5);
164
+ const k = MO.smooth(p), a0 = lerp(ym, y0, k), a1 = lerp(ym, y1, k);
165
+ c.beginPath(); c.moveTo(x, a0); c.quadraticCurveTo(x - depth * 0.5, a0, x - depth * 0.5, a0 + Math.min(40, (ym - a0) * 0.3));
166
+ c.lineTo(x - depth * 0.5, ym - Math.min(40, (ym - a0) * 0.3)); c.quadraticCurveTo(x - depth * 0.5, ym, x - depth, ym);
167
+ c.quadraticCurveTo(x - depth * 0.5, ym, x - depth * 0.5, ym + Math.min(40, (a1 - ym) * 0.3));
168
+ c.lineTo(x - depth * 0.5, a1 - Math.min(40, (a1 - ym) * 0.3)); c.quadraticCurveTo(x - depth * 0.5, a1, x, a1); c.stroke(); c.restore();
169
+ };
170
+
171
+ // ---------- 白板:笔画时间线 + 一支跟着画的笔 ----------
172
+ // 按顺序往板上加笔画,每笔时长 = 长度 / 笔速,自动排成时间线;画的时候笔尖永远在最新那一笔的前沿,两笔之间沿弧线滑过去,笔从不离开画面。
173
+ // const B = DG.board({ ink, lw, speed, font }); B.at(0.12); B.line(pts); B.text('猫?', x, y, 150, {rate: 7.5}); B.pop('×1000万张', x, y, 74); ...
174
+ // 每帧:const { tip, col } = B.draw(g, ft)(在世界坐标里画,调用方先套相机);const [sx, sy] = B.penAt(ft, cam, tip);DG.pen(c, sx, sy, col || B.penColor(ft))。
175
+ // 自定义笔画 B.push({ kind:'custom', t0, t1, col, draw(g, q, ft) → 笔尖|null, start() → 世界点, end() → 世界点, handed:true })。
176
+ // 速度口径(RSA 实测):正文写字 ≈16 字/秒、标题 ≈7.5 字/秒;强调色关键词不带手、整词弹出(后期合成)。
177
+ DG.board = ({ ink = '#0A0503', lw = 6.5, speed = 2600, font = '"LXGWWenKai-500"' } = {}) => {
178
+ const B = { S: [], cur: 0, ink, lw, speed, font };
179
+ B.at = t => { B.cur = t; return B; };
180
+ B.push = s => { B.S.push(s); return s; };
181
+ B.line = (raw, { w = lw, col = ink, speed: sp = speed, seed = B.S.length + 1, amp = 1.4, smooth = true, min = 0.04, gap = 0.02 } = {}) => {
182
+ const pts = smooth ? DG.hand(raw, { amp, seed, per: 8 }) : raw, cum = DG.cum(pts), len = cum[cum.length - 1];
183
+ const d = Math.max(min, len / sp); B.S.push({ kind: 'line', pts, cum, len, w, col, t0: B.cur, t1: B.cur + d }); B.cur += d + gap;
184
+ };
185
+ // 一笔直线(不抖、固定时长,比如网络连线),不推进太多时间
186
+ B.segment = (a, b, { w = 3, col = ink, dur = 0.06, gap = 0.012 } = {}) => {
187
+ const L = Math.hypot(b[0] - a[0], b[1] - a[1]); B.S.push({ kind: 'line', pts: [a, b], cum: [0, L], len: L, w, col, t0: B.cur, t1: B.cur + dur }); B.cur += gap;
188
+ };
189
+ // 手写字:rate 字/秒
190
+ B.text = (str, x, y, size, { col = ink, rate = 16, align = 'left', dur } = {}) => {
191
+ const d = dur || [...str].length / rate; B.S.push({ kind: 'text', str, x, y, size, col, align, t0: B.cur, t1: B.cur + d }); B.cur += d + 0.03;
192
+ };
193
+ // 强调词:不带手、0.3s 放大回落整词弹出
194
+ B.pop = (str, x, y, size, { col = ink, align = 'center', dur = 0.3 } = {}) => { B.S.push({ kind: 'pop', str, x, y, size, col, align, t0: B.cur, t1: B.cur + dur }); B.cur += 0.12; };
195
+ // 铺色(淡入,不推进时间)
196
+ B.fill = (path, col, dur = 0.18) => { B.S.push({ kind: 'fill', path, col, t0: B.cur, t1: B.cur + dur }); };
197
+
198
+ const drawOne = (g, s, ft) => {
199
+ if (ft < s.t0) return null;
200
+ const q = clamp((ft - s.t0) / (s.t1 - s.t0));
201
+ if (s.kind === 'custom') return s.draw(g, q, ft);
202
+ if (s.kind === 'fill') { g.save(); g.globalAlpha = q; g.fillStyle = s.col; g.fill(s.path); g.restore(); return null; }
203
+ if (s.kind === 'line') { g.strokeStyle = s.col; g.lineWidth = s.w; return DG.drawPartial(g, s.pts, s.cum, s.len * MO.sineInOut(q)); }
204
+ if (s.kind === 'pop') {
205
+ g.save(); g.font = `${s.size}px ${B.font}`; g.fillStyle = s.col; g.textBaseline = 'alphabetic'; g.textAlign = s.align;
206
+ const e = MO.backOut(q, 2.2); g.globalAlpha = clamp(q * 4); g.translate(s.x, s.y - s.size * 0.35); g.scale(e, e); g.fillText(s.str, 0, s.size * 0.35); g.restore(); return null;
207
+ }
208
+ if (s.kind === 'text') {
209
+ // 逐字写:笔尖在字框里 3 行之字形来回,遮罩跟着笔尖揭开这个字(Reveal 画法用在单字上)
210
+ g.save(); g.font = `${s.size}px ${B.font}`; g.fillStyle = s.col; g.textBaseline = 'alphabetic';
211
+ const chars = [...s.str], ws = chars.map(ch => g.measureText(ch).width), w = ws.reduce((a, b) => a + b, 0), x0 = s.align === 'center' ? s.x - w / 2 : s.x;
212
+ const n = chars.length, k = Math.min(n, q * n), done = Math.floor(k);
213
+ let x = x0, tip = null;
214
+ chars.forEach((ch, i) => {
215
+ if (i < done) g.fillText(ch, x, s.y);
216
+ else if (i === done && k < n) {
217
+ const zz = DG.zigzag(x - 2, s.y - s.size * 0.95, ws[i] + 4, s.size * 1.15, 3), cum = DG.cum(zz), d = cum[cum.length - 1] * (k - done);
218
+ g.save(); g.clip(DG.revealMask(zz, cum, d, s.size * 0.62)); g.fillText(ch, x, s.y); g.restore(); tip = DG.pointAt(zz, cum, d);
219
+ }
220
+ x += ws[i];
221
+ });
222
+ g.restore(); return tip || [x0 + w, s.y - s.size * 0.35];
223
+ }
224
+ return null;
225
+ };
226
+ // 画全部笔画(世界坐标)。返回正在画的那一笔的笔尖与墨色(没有在画的 → tip=null)
227
+ B.draw = (g, ft) => {
228
+ g.lineJoin = 'round'; g.lineCap = 'round';
229
+ let tip = null, col = null;
230
+ for (const s of B.S) { const p = drawOne(g, s, ft); if (p && ft < s.t1) { tip = p; col = s.col; } }
231
+ return { tip, col };
232
+ };
233
+ const handed = () => B.S.filter(s => s.kind !== 'pop' && s.kind !== 'fill' && (s.kind !== 'custom' || s.handed !== false));
234
+ const tw = s => s.size * [...s.str].length * 0.9;
235
+ const endPt = s => s.kind === 'custom' ? s.end() : s.kind === 'line' ? s.pts[s.pts.length - 1] : [s.align === 'center' ? s.x + tw(s) / 2 : s.x + tw(s), s.y - s.size * 0.3];
236
+ const startPt = s => s.kind === 'custom' ? s.start() : s.kind === 'line' ? s.pts[0] : [s.align === 'center' ? s.x - tw(s) / 2 : s.x, s.y - s.size * 0.3];
237
+ // 笔在屏幕上的位置:在画 → 笔尖;两笔之间 → 从上一笔终点沿弧线滑到下一笔起点;开场 → 从右下入画
238
+ B.penAt = (ft, cam, tip) => {
239
+ if (tip) return CAM.toScreen(cam, tip[0], tip[1]);
240
+ let prev = null, next = null;
241
+ for (const s of handed()) { if (s.t1 <= ft) prev = s; else if (s.t0 > ft && !next) next = s; }
242
+ if (prev && next) {
243
+ const q = MO.sineInOut((ft - prev.t1) / (next.t0 - prev.t1)), a = CAM.toScreen(cam, ...endPt(prev)), b = CAM.toScreen(cam, ...startPt(next));
244
+ return [lerp(a[0], b[0], q), lerp(a[1], b[1], q) - Math.sin(q * Math.PI) * Math.min(80, 20 + Math.hypot(b[0] - a[0], b[1] - a[1]) * 0.15)];
245
+ }
246
+ if (next) { const b = CAM.toScreen(cam, ...startPt(next)), q = MO.cubicOut(clamp(ft / next.t0)); return [lerp(W * 0.62, b[0], q), lerp(H * 0.7, b[1], q)]; }
247
+ return CAM.toScreen(cam, ...endPt(prev));
248
+ };
249
+ // 空档里笔的颜色 = 下一笔的墨色(没有下一笔就用上一笔)
250
+ B.penColor = ft => { const hs = handed(), nx = hs.find(s => s.t0 > ft), pv = [...hs].reverse().find(s => s.t1 <= ft); return (nx || pv || { col: ink }).col; };
251
+ return B;
252
+ };
253
+ // 一支马克笔(屏幕空间,笔尖在 (x,y),笔身朝右上)。笔尾帽 = 墨色;有高光和投影,浮在板上。用笔引导视线。
254
+ DG.pen = (c, x, y, col, tilt = 0) => {
255
+ c.save(); c.translate(x, y); c.rotate(-0.72 + tilt);
256
+ c.fillStyle = 'rgba(0,0,0,.12)'; c.beginPath(); c.roundRect(30, 14, 250, 34, 16); c.fill();
257
+ c.fillStyle = col; c.beginPath(); c.moveTo(0, 0); c.lineTo(26, -8); c.lineTo(26, 8); c.closePath(); c.fill();
258
+ c.fillStyle = '#e8e8e4'; c.beginPath(); c.roundRect(24, -12, 26, 24, 4); c.fill();
259
+ c.fillStyle = '#26262a'; c.beginPath(); c.roundRect(48, -17, 190, 34, 12); c.fill();
260
+ c.fillStyle = col; c.beginPath(); c.roundRect(212, -18, 46, 36, 10); c.fill();
261
+ c.fillStyle = 'rgba(255,255,255,.22)'; c.beginPath(); c.roundRect(60, -12, 140, 7, 4); c.fill();
262
+ c.restore();
263
+ };
264
+ })();
@@ -0,0 +1,70 @@
1
+ // 成片质感层(spec.finish):画完一帧之后、对整幅统一加的后期。所有语法通用;默认不开(clean)。
2
+ // spec.finish = 预设名 | { vignette, bloom, grain, contrast, saturate, warm }(对象里的键覆盖预设)。
3
+ // soft :轻暗角+极淡泛光+细颗粒——任何深色/浅色底都不显脏,大多数口播片的默认推荐
4
+ // film :暗角更重+泛光+可见颗粒+一点暖——叙事、故事型
5
+ // punch :对比+饱和各抬一点+轻暗角——发布会、财经、需要「抓眼」的段落
6
+ // clean :什么都不做(和不写一样)
7
+ // 键含义:vignette 暗角强度 0–0.6;bloom 泛光强度 0–0.4;grain 颗粒强度 0–0.12(按 24fps 换格,确定性);contrast/saturate 1=不变;warm 0–0.12 暖色罩。
8
+ // alpha 模式(透明背景)一律不加:后期会把透明区域染黑。
9
+ // 确定性:颗粒只由帧序号决定,同一时刻渲两次逐像素一致。
10
+ (() => {
11
+ const { clamp } = U;
12
+ const PRESETS = {
13
+ clean: {},
14
+ soft: { vignette: 0.2, bloom: 0.1, grain: 0.035 },
15
+ film: { vignette: 0.34, bloom: 0.12, grain: 0.07, warm: 0.04 },
16
+ punch: { vignette: 0.16, bloom: 0.08, contrast: 1.07, saturate: 1.1 },
17
+ };
18
+ const cache = {};
19
+ const scratch = (k, w, h) => { let c = cache[k]; if (!c || c.width !== w || c.height !== h) { c = cache[k] = document.createElement('canvas'); c.width = w; c.height = h; } return c; };
20
+ // 颗粒 tile:3 张 256×256 灰度噪声,种子固定
21
+ function grainTiles() {
22
+ if (cache.__grain) return cache.__grain;
23
+ const r = U.rng(20261016), tiles = [];
24
+ for (let k = 0; k < 3; k++) { const cv = document.createElement('canvas'); cv.width = cv.height = 256; const g = cv.getContext('2d'), im = g.createImageData(256, 256);
25
+ for (let i = 0; i < 256 * 256; i++) { const v = 128 + (r() + r() + r() - 1.5) * 90; im.data[i * 4] = im.data[i * 4 + 1] = im.data[i * 4 + 2] = v; im.data[i * 4 + 3] = 255; }
26
+ g.putImageData(im, 0, 0); tiles.push(cv); }
27
+ return (cache.__grain = tiles);
28
+ }
29
+ const FINISH = window.FINISH = {
30
+ PRESETS,
31
+ // 解析 spec.finish:返回完整参数对象或 null(不加)。写错预设名 → 抛错(clip.js 转成启动失败)
32
+ resolve(f) {
33
+ if (f == null || f === false || f === 'clean') return null;
34
+ let o = typeof f === 'string' ? PRESETS[f] : { ...(PRESETS[f.preset] || {}), ...f };
35
+ if (!o) throw new Error(`spec.finish「${f}」不是预设:${Object.keys(PRESETS).join(' / ')}(或写对象 {vignette, bloom, grain, contrast, saturate, warm})`);
36
+ const bad = Object.keys(o).filter(k => !['vignette', 'bloom', 'grain', 'contrast', 'saturate', 'warm', 'preset'].includes(k));
37
+ if (bad.length) throw new Error(`spec.finish 里没有这些键:${bad.join('、')}(可用 vignette bloom grain contrast saturate warm)`);
38
+ return Object.keys(o).length ? o : null;
39
+ },
40
+ apply(c, t, o, W, H, fps = 30) {
41
+ if (!o) return;
42
+ const cv = c.canvas;
43
+ // 对比/饱和:整幅过一遍 CSS filter
44
+ if ((o.contrast && o.contrast !== 1) || (o.saturate && o.saturate !== 1)) {
45
+ const s = scratch('fin_cs', W, H), g = s.getContext('2d'); g.setTransform(1, 0, 0, 1, 0, 0); g.clearRect(0, 0, W, H); g.drawImage(cv, 0, 0);
46
+ c.save(); c.setTransform(1, 0, 0, 1, 0, 0); c.filter = `contrast(${o.contrast || 1}) saturate(${o.saturate || 1})`; c.drawImage(s, 0, 0); c.restore();
47
+ }
48
+ // 泛光:缩小糊化后 screen 叠回(亮处晕开一点)
49
+ if (o.bloom > 0) {
50
+ const sw = Math.max(64, Math.round(W / 6)), sh = Math.max(64, Math.round(H / 6)), s = scratch('fin_bloom', sw, sh), g = s.getContext('2d');
51
+ g.setTransform(1, 0, 0, 1, 0, 0); g.clearRect(0, 0, sw, sh); g.filter = 'blur(5px) brightness(1.15)'; g.drawImage(cv, 0, 0, sw, sh); g.filter = 'none';
52
+ c.save(); c.setTransform(1, 0, 0, 1, 0, 0); c.globalCompositeOperation = 'screen'; c.globalAlpha = clamp(o.bloom, 0, 0.5); c.imageSmoothingQuality = 'high'; c.drawImage(s, 0, 0, W, H); c.restore();
53
+ }
54
+ // 暖色罩
55
+ if (o.warm > 0) { c.save(); c.setTransform(1, 0, 0, 1, 0, 0); c.globalCompositeOperation = 'soft-light'; c.fillStyle = `rgba(255,190,120,${clamp(o.warm, 0, 0.2) * 2})`; c.fillRect(0, 0, W, H); c.restore(); }
56
+ // 颗粒:24fps 换格,随机偏移;overlay 叠灰度噪声
57
+ if (o.grain > 0) {
58
+ const tiles = grainTiles(), f = Math.floor(t * 24 + 1e-6), tile = tiles[f % 3], r = U.rng(f * 7919 + 13), ox = Math.floor(r() * 256), oy = Math.floor(r() * 256);
59
+ c.save(); c.setTransform(1, 0, 0, 1, 0, 0); c.globalCompositeOperation = 'overlay'; c.globalAlpha = clamp(o.grain, 0, 0.2) * 3.2;
60
+ c.fillStyle = c.createPattern(tile, 'repeat'); c.translate(-ox, -oy); c.fillRect(ox, oy, W, H); c.restore();
61
+ }
62
+ // 暗角
63
+ if (o.vignette > 0) {
64
+ const R = Math.hypot(W, H) / 2, g = c.createRadialGradient(W / 2, H / 2, R * 0.45, W / 2, H / 2, R);
65
+ g.addColorStop(0, 'rgba(0,0,0,0)'); g.addColorStop(1, `rgba(0,0,0,${clamp(o.vignette, 0, 0.7)})`);
66
+ c.save(); c.setTransform(1, 0, 0, 1, 0, 0); c.fillStyle = g; c.fillRect(0, 0, W, H); c.restore();
67
+ }
68
+ },
69
+ };
70
+ })();
Binary file
Binary file
Binary file
Binary file
Binary file