dsh-motion 0.2.0 → 0.3.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.
package/NOTICE.md CHANGED
@@ -8,7 +8,9 @@
8
8
  | [Manim Community](https://github.com/ManimCommunity/manim) | MIT | 可选渲染后端 |
9
9
  | [manimgx](https://github.com/academa-labs/manimgx) | MIT | 默认渲染后端 |
10
10
  | [SymPy](https://github.com/sympy/sympy) | BSD-3-Clause | `formula_check` |
11
- | [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar) | MIT(风格文档/代码)· CC BY 4.0(demo 静帧) | `swiss-motion` / `dataviz` / `blueprint` 三个风格的改编来源;只取语法与规范,不打包 demo 代码 |
11
+ | [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar) | MIT(风格文档/代码)· CC BY 4.0(demo 静帧) | `swiss-motion` / `dataviz` / `blueprint` / `whiteboard` 的改编来源;只取语法与规范,不打包 demo 代码 |
12
+ | [subinium/3b1b-style-animation-skill](https://github.com/subinium/3b1b-style-animation-skill) | MIT | `3b1b` 的配色与教学法来源;非 3Blue1Brown 官方项目 |
13
+ | [adithya-s-k/manim_skill](https://github.com/adithya-s-k/manim_skill) | MIT | `3b1b` 的方法论参考(Manim 社区常用 skill) |
12
14
  | [FFmpeg](https://ffmpeg.org) | LGPL/GPL(取决于构建) | 渲染、后期、抽帧;**不打包**,由用户自行安装 |
13
15
 
14
16
  字体不随包分发;若你的 `DESIGN.md` 指定了第三方字体,请确认其许可(OFL / 商用授权)。
package/README.md CHANGED
@@ -34,25 +34,31 @@
34
34
 
35
35
  ## 风格库
36
36
 
37
- 默认 **瑞士动态排版(swiss-motion)**;另外两套按题材选。全部改编自 [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar)(MIT),不是自创。
37
+ 默认 **瑞士动态排版(swiss-motion)**;另外四套按题材选。四套改编自 [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar),`3b1b` 来自 Manim 社区,均非自创。
38
38
 
39
39
  | 风格 | 适合 | 规范 |
40
40
  |---|---|---|
41
41
  | `swiss-motion`(默认) | 公式、定义、数据结论 | [STYLE](styles/swiss-motion/STYLE.md) · [tokens](styles/swiss-motion/tokens.json) |
42
42
  | `dataviz` | 趋势、比较、分布、科学数据 | [STYLE](styles/dataviz/STYLE.md) · [tokens](styles/dataviz/tokens.json) |
43
43
  | `blueprint` | 几何证明、结构、尺寸关系 | [STYLE](styles/blueprint/STYLE.md) · [tokens](styles/blueprint/tokens.json) |
44
+ | `whiteboard` | 科学解释、课程、机制说明 | [STYLE](styles/whiteboard/STYLE.md) · [tokens](styles/whiteboard/tokens.json) |
45
+ | `3b1b` | 线代、微积分、概率、神经网络 | [STYLE](styles/3b1b/STYLE.md) · [tokens](styles/3b1b/tokens.json) |
44
46
 
45
47
  **参考与示例统一用 HyperFrames 的库**:[examples](https://hyperframes.heygen.com/examples)、[catalog](https://hyperframes.heygen.com/catalog)、[frame.md](https://hyperframes.heygen.com/prompting/design-systems)。本仓库不再内置样片/分集;`showcase/` 只放可复现的演示。
46
48
 
47
- 项目 `DESIGN.md` 头部写 `style: swiss-motion` + `style_version: 1.0.0`。风格来源、HF 集成、抽帧能力与 Lemo 调研见 [docs/ai-native-motion.md](docs/ai-native-motion.md)。
49
+ 路由是显式的:`node scripts/route-style.mjs "<题材>"`(点名优先 → registry 打分 → 默认 `swiss-motion`;分数并列/为 0 时要求确认)。规则在 [styles/registry.json](styles/registry.json)。
50
+
51
+ 项目 `DESIGN.md` 头部写 `style: <slug>` + `style_version: 1.0.0`。风格来源、HF 集成、抽帧能力与 Lemo 调研见 [docs/ai-native-motion.md](docs/ai-native-motion.md)。
48
52
 
49
53
  ## 演示
50
54
 
51
- 三个类别各一支 10 秒内短片,记录出帧/渲染耗时:
55
+ 五个类别各一支 10 秒内短片,记录出帧/渲染耗时:
52
56
 
53
57
  - 公式:[showcase/swiss-euler](showcase/swiss-euler)
54
58
  - 数据:[showcase/dataviz-secant](showcase/dataviz-secant)
55
59
  - 几何:[showcase/blueprint-pythagoras](showcase/blueprint-pythagoras)
60
+ - 白板:[showcase/whiteboard-derivative](showcase/whiteboard-derivative)
61
+ - 暗场数学:[showcase/3b1b-unit-circle](showcase/3b1b-unit-circle)
56
62
 
57
63
  汇总见 [showcase/README.md](showcase/README.md)。
58
64
 
@@ -186,8 +192,8 @@ npx hyperframes render -o out.mp4
186
192
  日常改代码**不需要重装**:link 安装一次后,改 `lib/ops.js` / `lib/doctor.js` 会在下一次工具调用时按 mtime 热加载;只有改工具名/参数 schema 才需要 `⌘⇧R`。风格、`tokens.json`、场景文件本来就是运行时读取,不用重启。详见 [docs/dev-loop.md](docs/dev-loop.md),也可以完全绕开 Harness:
187
193
 
188
194
  ```bash
189
- node scripts/dev.mjs frames showcase/swiss-euler/scene.py 1,3,end
190
- node scripts/dev.mjs render showcase/swiss-euler/scene.py --fps 30
195
+ node scripts/dev.mjs frames showcase/swiss-euler/scene.py 1,3,end # → out/swiss-euler-frames.png
196
+ node scripts/dev.mjs render showcase/swiss-euler/scene.py --fps 30 # → out/swiss-euler.mp4
191
197
  ```
192
198
 
193
199
  ## 发版(维护者)
@@ -53,7 +53,19 @@ HF 的卖点是 "same input, same frames, same output":HTML + 可 seek 的动
53
53
  - 可借鉴的三件事:`STYLE.md` 作为 style prompt、三张风格帧验收、分镜只在被要求时做;
54
54
  - 授权上,demo 静帧可商用但需署名 LemoLab;风格文件本身是 MIT。
55
55
 
56
- 本仓库的选择:删除自创的 `dark-editorial`,改为 3 套改编自 Lemo 的风格(`swiss-motion` / `dataviz` / `blueprint`),每套按 `STYLE.md` + `tokens.json` + `theme.py` + `preview.jpg` 落地;外部参考统一指向 HF。
56
+ 本仓库的选择:删除自创的 `dark-editorial`,改为 4 套 Lemo 风格(`swiss-motion` / `dataviz` / `blueprint` / `whiteboard`)+ 1 套 Manim 社区的 `3b1b`,每套按 `STYLE.md` + `tokens.json` + `theme.py` + `preview.jpg` 落地;外部参考统一指向 HF。
57
+
58
+ ### 3b1b 风格:社区里谁在用
59
+
60
+ 3Blue1Brown 本人用的是 ManimGL,官方没有发布“风格规范”。社区里把它固化成 agent skill 的主要有三家:
61
+
62
+ | 项目 | 规模 | 提供什么 |
63
+ |---|---|---|
64
+ | [adithya-s-k/manim_skill](https://github.com/adithya-s-k/manim_skill) | 约 1.2k stars | ManimCE / ManimGL 两套 best-practice skill,含大量可运行示例;是这类 skill 里用得最多的 |
65
+ | [Yusuke710/manim-skill](https://github.com/Yusuke710/manim-skill) | 约 180 stars | 面向 agent 的 Manim 动画 skill |
66
+ | [subinium/3b1b-style-animation-skill](https://github.com/subinium/3b1b-style-animation-skill) | 约 7 stars | 唯一把 3b1b 配色写成明确 token 的 skill(`#1c1c1c` / 蓝 `#3b82f6` / 黄 `#fbbf24`) |
67
+
68
+ 本仓库的 `3b1b` 取 subinium 的配色 + adithya 的方法论,按 manimgx 重写;不是 3Blue1Brown 官方项目。
57
69
 
58
70
  ## 3. 和 HyperFrames 怎么结合
59
71
 
@@ -177,6 +189,62 @@ DESIGN.md
177
189
 
178
190
  细节见 [dev-loop.md](dev-loop.md)。
179
191
 
192
+ ## 4.7 风格路由:目前怎么做,通用标准是什么
193
+
194
+ ### 本仓库
195
+
196
+ 没有隐藏的自动路由。风格在**写作阶段**确定,分三层:
197
+
198
+ 1. `styles/registry.json`:机器可读的风格目录(slug、中文名、uses、keywords、avoid、画幅、复杂度);
199
+ 2. `node scripts/route-style.mjs "<题材>"`:显式打分——点名风格 12 分,`keywords` 命中每个 3 分,`uses` 每个 2 分,命中 `avoid` 扣 2 分;
200
+ 3. `DESIGN.md` 头部 `style:` + `style_version:`:最终声明;场景代码从 `styles/<slug>/theme.py` 取 token。
201
+
202
+ 判定规则:
203
+
204
+ - 点名("用 3b1b"/"瑞士动态排版")→ 直接命中,最高优先;
205
+ - 否则打分;`high` 直接写,`medium`/`low`/并列 → 给 top 2–3 让用户确认;
206
+ - 完全无命中 → 回退 `default`(`swiss-motion`),并说明是默认。
207
+
208
+ 这样路由是**可复现的**:同样的题材和 registry 得到同样的推荐,不靠模型"感觉像"。
209
+
210
+ ### 通用做法(没有 ISO 标准,但有事实标准)
211
+
212
+ | 做法 | 代表 | 要点 |
213
+ |---|---|---|
214
+ | 风格索引 + 元数据 | Lemo `styles/README.md` + `style.json`;HF catalog | 每个风格有稳定 slug、中英文名、分类、`uses`;索引由元数据生成,不手写 |
215
+ | 规范文件做声明 | HF `frame.md`;本仓库 `DESIGN.md` | frontmatter 定 hex/字体,正文定 Do/Don't;agent 先读再画 |
216
+ | 点名优先 + 推荐 2–3 个 | Lemo `AGENTS.md` | 用户没选时给最接近的 2–3 个,不静默换风格 |
217
+ | 打分 / 检索路由 | registry 关键词;HF `catalog --query` 语义检索 | 关键词/描述匹配,输出带理由与置信度 |
218
+ | 人类确认门 | 本仓库 `needsConfirmation` | 低置信度、并列、冲突时不自动拍板 |
219
+ | 版本与许可 | `style_version`、MIT/CC BY 标注 | 风格可升级、可追溯,不把 demo 当规范 |
220
+
221
+ 没有"W3C 风格路由标准"这种东西;能算标准的是 **design tokens**(颜色/字体/间距的可交换格式)和 **DESIGN.md / frame.md** 这类声明文件。路由本身是产品约定,关键不是算法多复杂,而是**规则显式、结果可复现、低置信度会问人**。
222
+
223
+ ### 对齐事实标准:对齐什么,不对齐什么
224
+
225
+ **对齐(接口层)**
226
+
227
+ - `style.json` 采用 Lemo 的字段:`slug` / `num` / `en` / `cn` / `category_en` / `category_cn` / `uses` / `line` / `demo`,再扩展 `keywords` / `avoid` / `aspect` / `fps` / `complexity`;
228
+ - `styles/README.md` 的表和 `styles/registry.json` 从 `style.json` 生成,不再手写(对应 Lemo 的 `styleboard/build.py`);
229
+ - 选风格语义采用 Lemo `AGENTS.md` 的“点名优先 + 推荐 2–3 个 + 不静默换风格”,并加置信度门;
230
+ - 保留 `style_version` / `source` / `license`,保证可升级、可追溯。
231
+
232
+ **不对齐(实现层)**
233
+
234
+ - 不复制 Lemo 的 Canvas/WebGL demo 代码,也不改 manimgx 渲染链;
235
+ - 不采用 Lemo 的 24 fps、相机旅行、手写笔刷等实现细节,只取语法;
236
+ - 不把 `tokens.json` 强改成 W3C DTCG:目前没有外部 token 消费方,改了只会增加转换成本;将来要接 Figma/HF design tokens 时再加适配层;
237
+ - 不照搬 59 套风格,只接与数学/数据/科学相关的几套。
238
+
239
+ 这样既能在元数据、索引、选择语义上和生态互操作,又不会把实现绑死在别人的 demo 上。
240
+
241
+ ### 目前的局限
242
+
243
+ - 打分是关键词匹配,不理解语义;"导数"和"切线"可能一个命中 dataviz、一个命中 3b1b(已通过去掉 3b1b 的"导数"关键词缓解);
244
+ - 没做向量检索或多语言同义词;
245
+ - registry 的 `keywords` 需要随新题材维护;
246
+ - 画幅/音频/复杂度字段目前只记录,还没参与硬约束(比如 9:16 未验证的风格应直接拒绝)。
247
+
180
248
  ## 5. 现成的风格体系(2026-10 可直接采用)
181
249
 
182
250
  不必从零写风格文档。这个生态已经沉淀出几套公开、可复用的体系:
@@ -222,7 +290,7 @@ DESIGN.md
222
290
  | `pictogram-motion` | Sports events / Visual identity / Event openers | 不选:偏活动/体育,公式场景弱 |
223
291
  | `dark-keynote` | Product launches / SaaS / Keynotes | 不选:与 `swiss-motion` 的排版角色重叠 |
224
292
 
225
- 已落 **3 个核心**(`swiss-motion` / `dataviz` / `blueprint`);`whiteboard`、`iso-infographic` 留作可选,等有真实题材再验。这个列表是筛选结果,不是永久名单——风格帧验收不过就换。
293
+ 已落 **5 套**:`swiss-motion` / `dataviz` / `blueprint` / `whiteboard` + `3b1b`。`iso-infographic`、`one-line`、`ascii-crt` 仍是候选,等有对应题材再验;这个列表不是永久名单——风格帧验收不过就换。
226
294
 
227
295
  落地方式:
228
296
 
package/docs/dev-loop.md CHANGED
@@ -40,7 +40,7 @@ node scripts/dev.mjs probe out.mp4
40
40
  node scripts/dev.mjs doctor
41
41
  ```
42
42
 
43
- 它直接 import `lib/ops.js`,不经过插件注册,适合调参数、量耗时、跑回归。改完再让工具层调用同一份代码。
43
+ 它直接 import `lib/ops.js`,不经过插件注册,适合调参数、量耗时、跑回归。默认输出到 `out/`(已 git 忽略),不会弄脏场景目录;`showcase/` 里是提交的展示片,测试片不要放那里。
44
44
 
45
45
  ## 方案 3:把变化关在数据里
46
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-motion",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Code-generated math & science animations for DeepSeek Harness: frames-first preview, Manim clips, formula checks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -38,9 +38,12 @@
38
38
  },
39
39
  "scripts": {
40
40
  "check": "node --check lib/index.js && node --check lib/ops.js && node --check lib/env.js && node --check lib/doctor.js",
41
- "test": "node scripts/test-ops.mjs",
41
+ "test": "node scripts/test-ops.mjs && node scripts/test-route.mjs",
42
42
  "smoke": "node scripts/smoke.mjs",
43
43
  "dev": "node scripts/dev.mjs",
44
+ "route": "node scripts/route-style.mjs",
45
+ "styles:index": "node scripts/build-style-index.mjs",
46
+ "styles:check": "node scripts/build-style-index.mjs --check",
44
47
  "check:meta": "node scripts/check-meta.mjs",
45
48
  "prepack": "npm run check && npm test && npm run smoke"
46
49
  },
@@ -12,7 +12,19 @@ description: 做代码动画/科普视频的总入口:先判题材(关系还
12
12
  | 数学讲解 | 一个**关系** | 公式、坐标、推导、表格 |
13
13
  | 科普叙事 | 一个**意象** | 画面、比喻、节奏、材质 |
14
14
 
15
- ## 2. 选画面层
15
+ ## 2. 选风格
16
+
17
+ 先读 `styles/registry.json`,再按顺序:
18
+
19
+ 1. **用户点名**("用 3b1b / 瑞士动态排版")→ 直接用,不再打分;
20
+ 2. 没有点名 → `node scripts/route-style.mjs "<题材>" --json`;
21
+ 3. `confidence: high` → 写进 `DESIGN.md` 的 `style:`;
22
+ 4. `medium` / `low` / `needsConfirmation: true` → 给用户 top 2–3 和理由,等确认;
23
+ 5. 完全不命中 → 默认 `swiss-motion`,并说明这是默认。
24
+
25
+ 不要用"感觉像"选风格;理由是 registry 的关键词/uses 命中,不是形容词。
26
+
27
+ ## 3. 选画面层
16
28
 
17
29
  | 画面层 | 适合 | 不用它的理由 |
18
30
  |---|---|---|
@@ -22,7 +34,7 @@ description: 做代码动画/科普视频的总入口:先判题材(关系还
22
34
 
23
35
  三者都只是素材层;**需要交付时**再统一进 HyperFrames 工程,默认的极简模式不经过 HF。
24
36
 
25
- ## 3. 固定顺序(默认极简,先帧后片)
37
+ ## 4. 固定顺序(默认极简,先帧后片)
26
38
 
27
39
  ```
28
40
  1. DESIGN.md(design spec) ← 先定颜色/字体/禁用项/参考
@@ -38,7 +50,7 @@ description: 做代码动画/科普视频的总入口:先判题材(关系还
38
50
  `motion_frames` 内部是 `manimgx inspect`,只画请求的时刻并做版式检查,几秒出图;
39
51
  `motion_probe` 是给已有 mp4 做体检的,不要用它替代看帧。
40
52
 
41
- ## 4. 四条硬规则
53
+ ## 5. 四条硬规则
42
54
 
43
55
  1. **没有 DESIGN.md 或参考语法,不开工**——否则每次都是同一个默认 look;
44
56
  2. **默认先帧后片**:先用 `motion_frames` 把画面改对,再决定要不要渲染;
@@ -19,6 +19,10 @@ HyperFrames 的 look 文档原话:**不给外部输入,相同的 brief 每
19
19
  | 4 | 主题自身的世界 | ✅ 年代、材质、展示场景 |
20
20
  | 5 | 官方预设(preset) | ⚠️ 幻灯片语言,仅点名或做 deck 时用 |
21
21
 
22
+ ## 先路由
23
+
24
+ 写 spec 之前先确定风格:`node scripts/route-style.mjs "<题材>"`。点名风格优先;没有点名就按 `styles/registry.json` 打分;`high` 才直接写,`medium`/`low` 先给用户 top 2–3 确认。
25
+
22
26
  ## 最小形态
23
27
 
24
28
  ```markdown
@@ -60,7 +64,7 @@ DESIGN.md 写完不等于风格成立。挑 3 个关键时刻(开场、转折
60
64
  - 三张里至少一张是 signature shot;
61
65
  - 两轮还不对,就换更接近的参考语法,而不是继续调参。
62
66
 
63
- ## 六种已验证的语言
67
+ ## 八种已验证的语言
64
68
 
65
69
  | 语言 | 适合 | 关键手段 |
66
70
  |---|---|---|
@@ -70,15 +74,19 @@ DESIGN.md 写完不等于风格成立。挑 3 个关键时刻(开场、转折
70
74
  | 瑞士动态排版 | 公式 / 概念 | 12 栏网格、单一信号色、精确缓动 |
71
75
  | 数据叙事 | 数据 / 科学 | 图表操作即转场、注释钉在数据上、蓝红语义 |
72
76
  | 蓝图工程制图 | 几何 / 工程 | 青晒纸、白线、制图顺序、一次红印章 |
77
+ | 白板讲解 | 科学 / 课程 | 暖白板、三色马克笔、书写顺序 |
78
+ | 3b1b 暗场数学 | 线代 / 微积分 | 近黑底、蓝黄语义、先直觉后公式 |
73
79
 
74
- 本仓库现有三套,全部改编自 [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar)(MIT):
80
+ 本仓库现有五套,除 `3b1b` 外均改编自 [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar)(MIT):
75
81
 
76
82
  - [`styles/swiss-motion/`](../../styles/swiss-motion/):公式/排版,默认;
77
83
  - [`styles/dataviz/`](../../styles/dataviz/):图表/数据;
78
- - [`styles/blueprint/`](../../styles/blueprint/):几何/工程。
84
+ - [`styles/blueprint/`](../../styles/blueprint/):几何/工程;
85
+ - [`styles/whiteboard/`](../../styles/whiteboard/):科学/白板讲解;
86
+ - [`styles/3b1b/`](../../styles/3b1b/):暗场数学(Manim 社区 3b1b skill)。
79
87
 
80
88
  规范在 `STYLE.md`,数值在 `tokens.json`,`theme.py` 提供 `apply_config()` 与 `backdrop()`。
81
- 项目 `DESIGN.md` 里写 `style: swiss-motion`(或 `dataviz` / `blueprint`)与 `style_version: 1.0.0`。
89
+ 项目 `DESIGN.md` 里写 `style: swiss-motion`(或 `dataviz` / `blueprint` / `whiteboard` / `3b1b`)与 `style_version: 1.0.0`。
82
90
 
83
91
  参考与示例统一从 HyperFrames 取:[examples](https://hyperframes.heygen.com/examples)、
84
92
  [catalog](https://hyperframes.heygen.com/catalog)、[frame.md](https://hyperframes.heygen.com/prompting/design-systems)。
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: 3b1b
3
+ version: 1.0.0
4
+ source: https://github.com/subinium/3b1b-style-animation-skill
5
+ license: MIT(上游风格 skill);非 3Blue1Brown 官方项目
6
+ status: adapted
7
+ ---
8
+
9
+ # 3Blue1Brown Style · 3b1b
10
+
11
+ > 配色表取自 [subinium/3b1b-style-animation-skill](https://github.com/subinium/3b1b-style-animation-skill)(MIT);
12
+ > 教学法与动画模式参考 [adithya-s-k/manim_skill](https://github.com/adithya-s-k/manim_skill)(MIT,约 1.2k stars,是这类 skill 里用得最多的)。
13
+ > 本文件是改编,不是原样复制;只保留不变量,实现换成 manimgx。
14
+
15
+ ## 1. 定位
16
+
17
+ 先建立直觉,再给公式。画面为“暗场 + 蓝黄高亮”的数学解释:
18
+
19
+ - 适合:线性代数、微积分、概率、神经网络、算法;
20
+ - 不适合:没有“为什么”的产品宣传、角色叙事。
21
+
22
+ ## 2. 不变量
23
+
24
+ | 项 | 规则 |
25
+ |---|---|
26
+ | 底色 | `#1c1c1c`(近黑灰,**不用纯黑**) |
27
+ | 主色 | 蓝 `#3b82f6`:主对象、向量、节点 |
28
+ | 强调 | 黄 `#fbbf24`:高亮、结论、公式 |
29
+ | 语义色 | 绿 `#22c55e` 正 / 红 `#ef4444` 负 / 灰 `#9ca3af` 非活动 |
30
+ | 公式 | 黄色 `#fde047` 或白色;一屏最多 4–5 种颜色 |
31
+ | 字体 | 无衬线(Noto Sans);公式 New Computer Modern |
32
+ | 运动 | `Create` / `FadeIn` / `Transform`;相关元素**同时**出现(`LaggedStartMap` 式级联),不要一个个 `play()` |
33
+ | 收尾 | 结尾至少 `wait(2)`,给结论停留 |
34
+
35
+ ## 3. 教学法
36
+
37
+ 1. **Hook**:先问一个有意思的问题;
38
+ 2. **Intuition**:用图形建立直觉,先不写公式;
39
+ 3. **Concrete**:用一个具体数字走一遍;
40
+ 4. **Formalization**:最后才给公式/命名。
41
+
42
+ ## 4. manimgx 映射
43
+
44
+ - `theme.py`:`apply_config()` 设 `#1c1c1c`、`backdrop()` 返回平涂底;
45
+ - 用 `Axes`、`Dot`、`Arrow`、`Line`、`always_redraw` 做旋转/投影/变换;
46
+ - 用 `MathTex` 排版公式,`Text` 做标题与注释;
47
+ - manimgx 的 `Write` 已经足够接近 Manim 的 `Write`;不需要 LaTeX 环境。
48
+
49
+ ## 5. Do / Don't
50
+
51
+ **Do**
52
+
53
+ - 先画“正在发生什么”,再写“它叫什么”;
54
+ - 蓝=对象,黄=高亮/结论;颜色有语义;
55
+ - 一次只讲一个变换;结尾停 2 秒。
56
+
57
+ **Don't**
58
+
59
+ - 不用纯黑 `#000000`;
60
+ - 不用高饱和纯红/纯绿/纯蓝;
61
+ - 不在建立直觉前抛公式;
62
+ - 不把每个元素拆成单独的 `play()`。
63
+
64
+ ## 6. 与上游的差异
65
+
66
+ - 上游是 ManimCE/ManimGL skill,包含 40+ 条规则;这里只抽取配色、运动与教学法;
67
+ - 上游使用 Manim 的 `LaggedStartMap` 等 API;manimgx 用 `AnimationGroup`/`LaggedStart` 近似;
68
+ - 不是 3Blue1Brown 官方风格指南,只是社区通行做法的整理。
Binary file
@@ -0,0 +1,50 @@
1
+ {
2
+ "slug": "3b1b",
3
+ "en": "3Blue1Brown Style",
4
+ "cn": "3b1b 数学动画",
5
+ "category_en": "Math explainer",
6
+ "category_cn": "数学讲解",
7
+ "uses": [
8
+ "Linear algebra",
9
+ "Calculus",
10
+ "Probability",
11
+ "Neural networks"
12
+ ],
13
+ "source": "https://github.com/subinium/3b1b-style-animation-skill",
14
+ "secondary_source": "https://github.com/adithya-s-k/manim_skill",
15
+ "license": "MIT (style skills); unofficial tribute",
16
+ "implementation": "manimgx (theme.py + tokens.json)",
17
+ "num": "05",
18
+ "film": "Rotation on the Unit Circle",
19
+ "line": "A rotation on the unit circle that lands on e^{iθ} = cosθ + i sinθ.",
20
+ "line_cn": "单位圆上的一次旋转,收束到 e^{iθ} = cosθ + i sinθ。",
21
+ "uses_cn": [
22
+ "线代",
23
+ "微积分",
24
+ "概率",
25
+ "神经网络"
26
+ ],
27
+ "keywords": [
28
+ "向量",
29
+ "矩阵",
30
+ "特征值",
31
+ "旋转",
32
+ "复数",
33
+ "单位圆",
34
+ "线代",
35
+ "概率",
36
+ "微积分",
37
+ "神经网络"
38
+ ],
39
+ "avoid": [
40
+ "产品发布",
41
+ "纯排版"
42
+ ],
43
+ "demo": "showcase/3b1b-unit-circle",
44
+ "demo_duration_sec": 10.3,
45
+ "aspect": [
46
+ "16:9"
47
+ ],
48
+ "fps": 30,
49
+ "complexity": "medium"
50
+ }
@@ -0,0 +1,43 @@
1
+ """3b1b 1.0.0 — 3Blue1Brown 风格(Manim 社区通行做法)的 manimgx 实现层。
2
+
3
+ 来源与许可:subinium/3b1b-style-animation-skill(MIT),配色表取自其 color-scheme.md;
4
+ 方法论参考 adithya-s-k/manim_skill(MIT,1.2k stars)。不是 3Blue1Brown 官方项目。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+
12
+ import manimgx as m
13
+
14
+ TOKENS = json.loads(Path(__file__).with_name("tokens.json").read_text(encoding="utf-8"))
15
+
16
+ BG = TOKENS["colors"]["bg"]
17
+ TEXT = TOKENS["colors"]["text"]
18
+ LABEL = TOKENS["colors"]["label"]
19
+ BLUE = TOKENS["colors"]["blue"]
20
+ YELLOW = TOKENS["colors"]["yellow"]
21
+ TEAL = TOKENS["colors"]["teal"]
22
+ GREEN = TOKENS["colors"]["green"]
23
+ RED = TOKENS["colors"]["red"]
24
+ GRAY = TOKENS["colors"]["gray"]
25
+ MATH = TOKENS["colors"]["math"]
26
+ AXIS = TOKENS["colors"]["axis"]
27
+ LAYOUT = TOKENS["layout"]
28
+
29
+
30
+ def apply_config(width: int = 1920, height: int = 1080, fps: int = 30) -> None:
31
+ m.config.pixel_width = width
32
+ m.config.pixel_height = height
33
+ m.config.frame_rate = fps
34
+ m.config.background_color = BG
35
+
36
+
37
+ def backdrop() -> m.Rectangle:
38
+ """近黑灰底;3b1b 规定不用纯黑。"""
39
+ return (
40
+ m.Rectangle(width=LAYOUT["frame"]["width"], height=LAYOUT["frame"]["height"], color=BG)
41
+ .set_fill(BG, 1)
42
+ .set_stroke(width=0)
43
+ )
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "3b1b",
3
+ "version": "1.0.0",
4
+ "source": "https://github.com/subinium/3b1b-style-animation-skill",
5
+ "license": "MIT (style skill); unofficial tribute to 3Blue1Brown",
6
+ "colors": {
7
+ "bg": "#1c1c1c",
8
+ "text": "#FFFFFF",
9
+ "label": "#E5E5E5",
10
+ "blue": "#3b82f6",
11
+ "yellow": "#fbbf24",
12
+ "teal": "#14b8a6",
13
+ "green": "#22c55e",
14
+ "red": "#ef4444",
15
+ "gray": "#9ca3af",
16
+ "math": "#fde047",
17
+ "axis": "#5A5A5A"
18
+ },
19
+ "typography": {
20
+ "display": { "family": "Noto Sans", "weight": "BOLD", "size": 46 },
21
+ "body": { "family": "Noto Sans", "size": 28 },
22
+ "mono": { "family": "Menlo", "size": 18 },
23
+ "math": { "family": "New Computer Modern", "size": 64 }
24
+ },
25
+ "layout": {
26
+ "aspect": "16:9",
27
+ "frame": { "width": 14.222, "height": 8.0 },
28
+ "margin": 0.5
29
+ },
30
+ "motion": { "fps": 30, "easing": "smooth", "hold_min_seconds": 2.0 },
31
+ "audio": { "target_lufs": -14, "true_peak_db": -1 }
32
+ }
package/styles/README.md CHANGED
@@ -1,23 +1,28 @@
1
1
  # 风格库
2
2
 
3
- 可复用的视觉语言。当前有 3 套,全部**改编自 Lemo-Opuscar** 的公开风格(MIT),不是自创:
3
+ 可复用的视觉语言。当前有 5 套:4 套改编自 [Lemo-Opuscar](https://github.com/lemomo-ai/lemo-opuscar)(MIT),1 套来自 Manim 社区的 3b1b skill。都不是自创。`style.json` 是唯一事实来源;下面的表和 `registry.json` 由 `node scripts/build-style-index.mjs` 生成,不手改:
4
4
 
5
+ <!-- styles:start -->
5
6
  | 风格 | 适合 | 来源 | 入口 |
6
7
  |---|---|---|---|
7
- | `swiss-motion` / 瑞士动态排版(默认) | 公式、定义、数据结论 | [Lemo](https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/swiss-motion) | [STYLE](swiss-motion/STYLE.md) · [tokens](swiss-motion/tokens.json) · [theme](swiss-motion/theme.py) |
8
- | `dataviz` / 数据叙事 | 趋势、比较、分布、科学数据 | [Lemo](https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/dataviz) | [STYLE](dataviz/STYLE.md) · [tokens](dataviz/tokens.json) · [theme](dataviz/theme.py) |
9
- | `blueprint` / 蓝图工程制图 | 几何证明、结构、尺寸关系 | [Lemo](https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/blueprint) | [STYLE](blueprint/STYLE.md) · [tokens](blueprint/tokens.json) · [theme](blueprint/theme.py) |
8
+ | `swiss-motion` / 瑞士动态排版(默认) | 公式、定义、数据结论、排版 | Lemo-Opuscar | [STYLE](swiss-motion/STYLE.md) · [tokens](swiss-motion/tokens.json) · [theme](swiss-motion/theme.py) |
9
+ | `dataviz` / 数据叙事 | 趋势、比较、分布、科学数据 | Lemo-Opuscar | [STYLE](dataviz/STYLE.md) · [tokens](dataviz/tokens.json) · [theme](dataviz/theme.py) |
10
+ | `blueprint` / 蓝图工程制图 | 几何证明、结构、尺寸关系 | Lemo-Opuscar | [STYLE](blueprint/STYLE.md) · [tokens](blueprint/tokens.json) · [theme](blueprint/theme.py) |
11
+ | `whiteboard` / 白板讲解 | 科学解释、课程、机制说明 | Lemo-Opuscar | [STYLE](whiteboard/STYLE.md) · [tokens](whiteboard/tokens.json) · [theme](whiteboard/theme.py) |
12
+ | `3b1b` / 3b1b 数学动画 | 线代、微积分、概率、神经网络 | subinium + manim_skill | [STYLE](3b1b/STYLE.md) · [tokens](3b1b/tokens.json) · [theme](3b1b/theme.py) |
13
+ <!-- styles:end -->
10
14
 
11
15
  ## 约定
12
16
 
13
17
  - 每个风格目录:`style.json`(元数据)+ `STYLE.md`(不变量)+ `tokens.json` + `theme.py` + `preview.jpg`;
14
18
  - `STYLE.md` 是**改编**,必须在上游基础上写清楚差异;不复制 demo 代码;
15
19
  - 颜色、字体、时间表、音频以 `tokens.json` 为准;版面坐标与组件配方写在 `STYLE.md`;
16
- - 新片在自己的项目目录写 `DESIGN.md`,头部 `style: swiss-motion` + `style_version: 1.0.0`;
20
+ - 路由:`styles/registry.json` 是元数据,`node scripts/route-style.mjs "<题材>"` 打分;点名优先,低置信度/并列先问用户;
21
+ - 新片在自己的项目目录写 `DESIGN.md`,头部 `style: <slug>` + `style_version: 1.0.0`;
17
22
  - 参考与示例统一用 HyperFrames 的 [examples](https://hyperframes.heygen.com/examples) / [catalog](https://hyperframes.heygen.com/catalog),不在仓库里放样片(`showcase/` 只放可复现的演示)。
18
23
 
19
24
  ## 许可
20
25
 
21
- - 上游风格文档:MIT,署名 LemoLab;
26
+ - 上游风格文档:Lemo-Opuscar / subinium / adithya-s-k 均为 MIT,分别署名;
22
27
  - 上游 demo 静帧:CC BY 4.0,商用需署名;
23
28
  - 本仓库的 `theme.py` / `tokens.json` / 场景代码:MIT。
@@ -1,11 +1,47 @@
1
1
  {
2
2
  "slug": "blueprint",
3
3
  "en": "Blueprint",
4
- "cn": "蓝图 / 工程制图",
4
+ "cn": "蓝图工程制图",
5
5
  "category_en": "Graphic & Type",
6
6
  "category_cn": "图形与排版",
7
- "uses": ["Engineering", "Hardware launches", "Tech explainers"],
7
+ "uses": [
8
+ "Geometry proof",
9
+ "Structures",
10
+ "Dimensions"
11
+ ],
8
12
  "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/blueprint",
9
13
  "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
10
- "implementation": "manimgx (theme.py + tokens.json)"
14
+ "implementation": "manimgx (theme.py + tokens.json)",
15
+ "num": "03",
16
+ "film": "Pythagorean Triple",
17
+ "line": "A 3-4-5 triangle drawn in drafting order, stamped Q.E.D.",
18
+ "line_cn": "按制图顺序画出的 3-4-5 三角形,最后盖上 Q.E.D.",
19
+ "uses_cn": [
20
+ "几何证明",
21
+ "结构",
22
+ "尺寸关系"
23
+ ],
24
+ "keywords": [
25
+ "几何",
26
+ "证明",
27
+ "三角形",
28
+ "圆",
29
+ "角度",
30
+ "尺寸",
31
+ "工程",
32
+ "结构",
33
+ "作图",
34
+ "勾股"
35
+ ],
36
+ "avoid": [
37
+ "图表",
38
+ "手绘"
39
+ ],
40
+ "demo": "showcase/blueprint-pythagoras",
41
+ "demo_duration_sec": 6.9,
42
+ "aspect": [
43
+ "16:9"
44
+ ],
45
+ "fps": 30,
46
+ "complexity": "medium"
11
47
  }
@@ -4,8 +4,47 @@
4
4
  "cn": "数据叙事",
5
5
  "category_en": "Information & Keynote",
6
6
  "category_cn": "信息与发布",
7
- "uses": ["Data journalism", "Reports", "Climate & science"],
7
+ "uses": [
8
+ "Trends",
9
+ "Comparison",
10
+ "Distribution",
11
+ "Scientific data"
12
+ ],
8
13
  "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/dataviz",
9
14
  "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
10
- "implementation": "manimgx (theme.py + tokens.json)"
15
+ "implementation": "manimgx (theme.py + tokens.json)",
16
+ "num": "02",
17
+ "film": "Secant to Tangent",
18
+ "line": "A secant slides into a tangent; the slope reading falls from 2.50 to 2.00.",
19
+ "line_cn": "割线滑成切线,斜率读数从 2.50 落到 2.00。",
20
+ "uses_cn": [
21
+ "趋势",
22
+ "比较",
23
+ "分布",
24
+ "科学数据"
25
+ ],
26
+ "keywords": [
27
+ "数据",
28
+ "图表",
29
+ "趋势",
30
+ "比较",
31
+ "分布",
32
+ "斜率",
33
+ "切线",
34
+ "割线",
35
+ "函数",
36
+ "变化率",
37
+ "统计"
38
+ ],
39
+ "avoid": [
40
+ "纯文字排版",
41
+ "角色叙事"
42
+ ],
43
+ "demo": "showcase/dataviz-secant",
44
+ "demo_duration_sec": 10.1,
45
+ "aspect": [
46
+ "16:9"
47
+ ],
48
+ "fps": 30,
49
+ "complexity": "low"
11
50
  }
@@ -0,0 +1,212 @@
1
+ {
2
+ "version": 1,
3
+ "generated": true,
4
+ "default": "swiss-motion",
5
+ "styles": [
6
+ {
7
+ "slug": "swiss-motion",
8
+ "num": "01",
9
+ "name_cn": "瑞士动态排版",
10
+ "name_en": "Swiss Motion Graphics",
11
+ "category_cn": "图形与排版",
12
+ "uses": [
13
+ "Formula",
14
+ "Definition",
15
+ "Data conclusion",
16
+ "Typography"
17
+ ],
18
+ "uses_cn": [
19
+ "公式",
20
+ "定义",
21
+ "数据结论",
22
+ "排版"
23
+ ],
24
+ "keywords": [
25
+ "公式",
26
+ "欧拉",
27
+ "定理",
28
+ "定义",
29
+ "恒等式",
30
+ "推导",
31
+ "排版",
32
+ "方程"
33
+ ],
34
+ "avoid": [
35
+ "图表",
36
+ "几何证明"
37
+ ],
38
+ "aspect": [
39
+ "16:9"
40
+ ],
41
+ "fps": 30,
42
+ "complexity": "low",
43
+ "demo": "showcase/swiss-euler",
44
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/swiss-motion"
45
+ },
46
+ {
47
+ "slug": "dataviz",
48
+ "num": "02",
49
+ "name_cn": "数据叙事",
50
+ "name_en": "Data Storytelling",
51
+ "category_cn": "信息与发布",
52
+ "uses": [
53
+ "Trends",
54
+ "Comparison",
55
+ "Distribution",
56
+ "Scientific data"
57
+ ],
58
+ "uses_cn": [
59
+ "趋势",
60
+ "比较",
61
+ "分布",
62
+ "科学数据"
63
+ ],
64
+ "keywords": [
65
+ "数据",
66
+ "图表",
67
+ "趋势",
68
+ "比较",
69
+ "分布",
70
+ "斜率",
71
+ "切线",
72
+ "割线",
73
+ "函数",
74
+ "变化率",
75
+ "统计"
76
+ ],
77
+ "avoid": [
78
+ "纯文字排版",
79
+ "角色叙事"
80
+ ],
81
+ "aspect": [
82
+ "16:9"
83
+ ],
84
+ "fps": 30,
85
+ "complexity": "low",
86
+ "demo": "showcase/dataviz-secant",
87
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/dataviz"
88
+ },
89
+ {
90
+ "slug": "blueprint",
91
+ "num": "03",
92
+ "name_cn": "蓝图工程制图",
93
+ "name_en": "Blueprint",
94
+ "category_cn": "图形与排版",
95
+ "uses": [
96
+ "Geometry proof",
97
+ "Structures",
98
+ "Dimensions"
99
+ ],
100
+ "uses_cn": [
101
+ "几何证明",
102
+ "结构",
103
+ "尺寸关系"
104
+ ],
105
+ "keywords": [
106
+ "几何",
107
+ "证明",
108
+ "三角形",
109
+ "圆",
110
+ "角度",
111
+ "尺寸",
112
+ "工程",
113
+ "结构",
114
+ "作图",
115
+ "勾股"
116
+ ],
117
+ "avoid": [
118
+ "图表",
119
+ "手绘"
120
+ ],
121
+ "aspect": [
122
+ "16:9"
123
+ ],
124
+ "fps": 30,
125
+ "complexity": "medium",
126
+ "demo": "showcase/blueprint-pythagoras",
127
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/blueprint"
128
+ },
129
+ {
130
+ "slug": "whiteboard",
131
+ "num": "04",
132
+ "name_cn": "白板讲解",
133
+ "name_en": "Whiteboard Explainer",
134
+ "category_cn": "手绘与绘画",
135
+ "uses": [
136
+ "Explainers",
137
+ "Online courses",
138
+ "Science"
139
+ ],
140
+ "uses_cn": [
141
+ "科学解释",
142
+ "课程",
143
+ "机制说明"
144
+ ],
145
+ "keywords": [
146
+ "讲解",
147
+ "课程",
148
+ "科学",
149
+ "机制",
150
+ "流程",
151
+ "为什么",
152
+ "原理",
153
+ "物理",
154
+ "化学",
155
+ "生物"
156
+ ],
157
+ "avoid": [
158
+ "高密度公式",
159
+ "暗场"
160
+ ],
161
+ "aspect": [
162
+ "16:9"
163
+ ],
164
+ "fps": 30,
165
+ "complexity": "medium",
166
+ "demo": "showcase/whiteboard-derivative",
167
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/whiteboard"
168
+ },
169
+ {
170
+ "slug": "3b1b",
171
+ "num": "05",
172
+ "name_cn": "3b1b 数学动画",
173
+ "name_en": "3Blue1Brown Style",
174
+ "category_cn": "数学讲解",
175
+ "uses": [
176
+ "Linear algebra",
177
+ "Calculus",
178
+ "Probability",
179
+ "Neural networks"
180
+ ],
181
+ "uses_cn": [
182
+ "线代",
183
+ "微积分",
184
+ "概率",
185
+ "神经网络"
186
+ ],
187
+ "keywords": [
188
+ "向量",
189
+ "矩阵",
190
+ "特征值",
191
+ "旋转",
192
+ "复数",
193
+ "单位圆",
194
+ "线代",
195
+ "概率",
196
+ "微积分",
197
+ "神经网络"
198
+ ],
199
+ "avoid": [
200
+ "产品发布",
201
+ "纯排版"
202
+ ],
203
+ "aspect": [
204
+ "16:9"
205
+ ],
206
+ "fps": 30,
207
+ "complexity": "medium",
208
+ "demo": "showcase/3b1b-unit-circle",
209
+ "source": "https://github.com/subinium/3b1b-style-animation-skill"
210
+ }
211
+ ]
212
+ }
@@ -4,8 +4,44 @@
4
4
  "cn": "瑞士动态排版",
5
5
  "category_en": "Graphic & Type",
6
6
  "category_cn": "图形与排版",
7
- "uses": ["Brand identity", "Events", "Design education"],
7
+ "uses": [
8
+ "Formula",
9
+ "Definition",
10
+ "Data conclusion",
11
+ "Typography"
12
+ ],
8
13
  "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/swiss-motion",
9
14
  "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
10
- "implementation": "manimgx (theme.py + tokens.json)"
15
+ "implementation": "manimgx (theme.py + tokens.json)",
16
+ "num": "01",
17
+ "film": "Euler Formula",
18
+ "line": "A formula on a Swiss grid: one red accent, five constants.",
19
+ "line_cn": "瑞士网格上的一个公式:一个红色信号色,五个常数。",
20
+ "uses_cn": [
21
+ "公式",
22
+ "定义",
23
+ "数据结论",
24
+ "排版"
25
+ ],
26
+ "keywords": [
27
+ "公式",
28
+ "欧拉",
29
+ "定理",
30
+ "定义",
31
+ "恒等式",
32
+ "推导",
33
+ "排版",
34
+ "方程"
35
+ ],
36
+ "avoid": [
37
+ "图表",
38
+ "几何证明"
39
+ ],
40
+ "demo": "showcase/swiss-euler",
41
+ "demo_duration_sec": 6.2,
42
+ "aspect": [
43
+ "16:9"
44
+ ],
45
+ "fps": 30,
46
+ "complexity": "low"
11
47
  }
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: whiteboard
3
+ version: 1.0.0
4
+ source: https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/whiteboard
5
+ license: MIT(上游风格文档);demo 静帧 CC BY 4.0(LemoLab)
6
+ status: adapted
7
+ ---
8
+
9
+ # Whiteboard Explainer · 白板讲解
10
+
11
+ > 上游:Lemo-Opuscar `styles/whiteboard`(MIT)。保留“一块板 + 三支马克笔 + 书写顺序”的语法;
12
+ > manimgx 版省掉手/浮空笔/相机旅行,用 draw-on 与固定机位近似。
13
+
14
+ ## 1. 定位
15
+
16
+ 一块暖白干擦板,所有内容都是马克笔“写”出来的:
17
+
18
+ - 适合:科学解释、课程、机制/流程说明;
19
+ - 不适合:需要真实材质、角色表演、复杂 3D 的题材。
20
+
21
+ ## 2. 不变量
22
+
23
+ | 项 | 规则 |
24
+ |---|---|
25
+ | 板面 | 暖白 `#F7F5EF`,可有轻微不均与旧字痕 |
26
+ | 墨色 | 黑 `#23262C`:结构与文字 |
27
+ | 过程色 | 蓝 `#2A5CB3`:测量、过程、正在变化 |
28
+ | 强调 | 橙 `#D97757`:结论、最终答案;每区最多一次 |
29
+ | 字体 | 手写体(Chalkboard SE);公式 New Computer Modern |
30
+ | 运动 | 按书写顺序 draw-on;一笔接一笔;不弹跳 |
31
+ | 版面 | 一个想法一个区域;距离=论证距离;标签要么全在画面内,要么不出现 |
32
+
33
+ ## 3. manimgx 映射
34
+
35
+ - `theme.py`:`apply_config()`、`backdrop()`(暖白渐变 + 细噪点);
36
+ - 线条与文字用 `Create` / `Write` 按顺序出现;没有笔、手、橡皮、相机;
37
+ - 字体替换:EMS Tech / Architects Daughter → `Chalkboard SE`;
38
+ - 旧字痕用低透明度 `Text`/`Line` 近似。
39
+
40
+ ## 4. Do / Don't
41
+
42
+ **Do**
43
+
44
+ - 先写标题,再按“定义 → 例子 → 结论”在板面上展开;
45
+ - 蓝色只给“正在变化”的对象,橙色只给结论;
46
+ - 结论出现后停住,给读者读完。
47
+
48
+ **Don't**
49
+
50
+ - 不画手、笔、光标;
51
+ - 不用第四种马克笔颜色;
52
+ - 不做镜头急推/急拉;文字不压线。
53
+
54
+ ## 5. 与上游的差异
55
+
56
+ - 上游有浮空马克笔、笔影、干擦纹理、相机旅行;这里只保留书写顺序与三色语义;
57
+ - 上游 24 fps、板面可远大于画幅;本实现 30 fps、固定画幅;
58
+ - 字体与纹理均为近似,验收以帧为准。
Binary file
@@ -0,0 +1,47 @@
1
+ {
2
+ "slug": "whiteboard",
3
+ "en": "Whiteboard Explainer",
4
+ "cn": "白板讲解",
5
+ "category_en": "Hand-drawn & Painting",
6
+ "category_cn": "手绘与绘画",
7
+ "uses": [
8
+ "Explainers",
9
+ "Online courses",
10
+ "Science"
11
+ ],
12
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/whiteboard",
13
+ "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
14
+ "implementation": "manimgx (theme.py + tokens.json)",
15
+ "num": "04",
16
+ "film": "Secant on a Whiteboard",
17
+ "line": "A derivative explained on one warm whiteboard with three markers.",
18
+ "line_cn": "在一块暖白板上,用三支马克笔讲清导数。",
19
+ "uses_cn": [
20
+ "科学解释",
21
+ "课程",
22
+ "机制说明"
23
+ ],
24
+ "keywords": [
25
+ "讲解",
26
+ "课程",
27
+ "科学",
28
+ "机制",
29
+ "流程",
30
+ "为什么",
31
+ "原理",
32
+ "物理",
33
+ "化学",
34
+ "生物"
35
+ ],
36
+ "avoid": [
37
+ "高密度公式",
38
+ "暗场"
39
+ ],
40
+ "demo": "showcase/whiteboard-derivative",
41
+ "demo_duration_sec": 9.6,
42
+ "aspect": [
43
+ "16:9"
44
+ ],
45
+ "fps": 30,
46
+ "complexity": "medium"
47
+ }
@@ -0,0 +1,53 @@
1
+ """whiteboard 1.0.0 — Lemo-Opuscar 的 Whiteboard Explainer,manimgx 实现层。
2
+
3
+ 上游规范(MIT):styles/whiteboard/STYLE.md
4
+ 暖白板 + 三支马克笔;manimgx 版省掉笔/手/相机旅行,只保留书写与结构。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+
12
+ import numpy as np
13
+
14
+ import manimgx as m
15
+
16
+ TOKENS = json.loads(Path(__file__).with_name("tokens.json").read_text(encoding="utf-8"))
17
+
18
+ BOARD = TOKENS["colors"]["board"]
19
+ BOARD_EDGE = TOKENS["colors"]["board_edge"]
20
+ INK = TOKENS["colors"]["ink"]
21
+ BLUE = TOKENS["colors"]["blue"]
22
+ ORANGE = TOKENS["colors"]["orange"]
23
+ MUTED = TOKENS["colors"]["muted"]
24
+ GHOST = TOKENS["colors"]["ghost"]
25
+ LAYOUT = TOKENS["layout"]
26
+
27
+
28
+ def apply_config(width: int = 1920, height: int = 1080, fps: int = 30) -> None:
29
+ m.config.pixel_width = width
30
+ m.config.pixel_height = height
31
+ m.config.frame_rate = fps
32
+ m.config.background_color = BOARD
33
+
34
+
35
+ def backdrop(seed: int = 5) -> m.ImageMobject:
36
+ """暖白板:轻微渐变 + 细噪点,避免大块纯白。"""
37
+ w, h = m.config.pixel_width, m.config.pixel_height
38
+ yy, xx = np.mgrid[0:h, 0:w]
39
+ u = xx / (w - 1)
40
+ v = yy / (h - 1)
41
+ glow = np.exp(-(((u - 0.45) ** 2) / 0.30 + ((v - 0.42) ** 2) / 0.30))
42
+ rgb = np.empty((h, w, 3), dtype=float)
43
+ base = np.array([247, 245, 239], dtype=float)
44
+ edge = np.array([233, 228, 216], dtype=float)
45
+ rgb[:] = edge + glow[..., None] * (base - edge)
46
+ rng = np.random.default_rng(seed)
47
+ rgb = np.clip(rgb + rng.normal(0, 1.0, rgb.shape), 0, 255).astype(np.uint8)
48
+ img = m.ImageMobject(rgb, scale_to_resolution=min(w, h), resampling_algorithm=3)
49
+ return img.move_to(m.ORIGIN)
50
+
51
+
52
+ def board_line(y: float, x0: float, x1: float, color: str = INK, width: float = 2.4) -> m.Line:
53
+ return m.Line([x0, y, 0], [x1, y, 0], color=color, stroke_width=width)
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "whiteboard",
3
+ "version": "1.0.0",
4
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/whiteboard",
5
+ "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
6
+ "colors": {
7
+ "board": "#F7F5EF",
8
+ "board_edge": "#E9E4D8",
9
+ "ink": "#23262C",
10
+ "blue": "#2A5CB3",
11
+ "orange": "#D97757",
12
+ "muted": "#8A857C",
13
+ "ghost": "#C9C3B6"
14
+ },
15
+ "typography": {
16
+ "hand": { "family": "Chalkboard SE", "size": 34 },
17
+ "label": { "family": "Chalkboard SE", "size": 24 },
18
+ "math": { "family": "New Computer Modern", "size": 54 }
19
+ },
20
+ "layout": {
21
+ "aspect": "16:9",
22
+ "frame": { "width": 14.222, "height": 8.0 },
23
+ "margin": 0.55
24
+ },
25
+ "motion": { "fps": 30, "easing": "hand-speed", "hold_min_seconds": 1.4 },
26
+ "audio": { "target_lufs": -14, "true_peak_db": -1 }
27
+ }