dsh-motion 0.1.1 → 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.
Files changed (39) hide show
  1. package/NOTICE.md +3 -0
  2. package/README.md +114 -25
  3. package/docs/ai-native-motion.md +301 -0
  4. package/docs/dev-loop.md +55 -0
  5. package/lib/doctor.js +26 -1
  6. package/lib/fresh.js +21 -0
  7. package/lib/index.js +44 -6
  8. package/lib/ops.js +164 -8
  9. package/package.json +10 -3
  10. package/skills/animation-router/SKILL.md +30 -12
  11. package/skills/design-spec/SKILL.md +33 -1
  12. package/skills/qa-rubric/SKILL.md +14 -8
  13. package/styles/3b1b/STYLE.md +68 -0
  14. package/styles/3b1b/preview.jpg +0 -0
  15. package/styles/3b1b/style.json +50 -0
  16. package/styles/3b1b/theme.py +43 -0
  17. package/styles/3b1b/tokens.json +32 -0
  18. package/styles/README.md +28 -0
  19. package/styles/blueprint/STYLE.md +59 -0
  20. package/styles/blueprint/preview.jpg +0 -0
  21. package/styles/blueprint/style.json +47 -0
  22. package/styles/blueprint/theme.py +65 -0
  23. package/styles/blueprint/tokens.json +27 -0
  24. package/styles/dataviz/STYLE.md +58 -0
  25. package/styles/dataviz/preview.jpg +0 -0
  26. package/styles/dataviz/style.json +50 -0
  27. package/styles/dataviz/theme.py +49 -0
  28. package/styles/dataviz/tokens.json +31 -0
  29. package/styles/registry.json +212 -0
  30. package/styles/swiss-motion/STYLE.md +59 -0
  31. package/styles/swiss-motion/preview.jpg +0 -0
  32. package/styles/swiss-motion/style.json +47 -0
  33. package/styles/swiss-motion/theme.py +59 -0
  34. package/styles/swiss-motion/tokens.json +33 -0
  35. package/styles/whiteboard/STYLE.md +58 -0
  36. package/styles/whiteboard/preview.jpg +0 -0
  37. package/styles/whiteboard/style.json +47 -0
  38. package/styles/whiteboard/theme.py +53 -0
  39. package/styles/whiteboard/tokens.json +27 -0
@@ -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
+ }
@@ -0,0 +1,28 @@
1
+ # 风格库
2
+
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
+
5
+ <!-- styles:start -->
6
+ | 风格 | 适合 | 来源 | 入口 |
7
+ |---|---|---|---|
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 -->
14
+
15
+ ## 约定
16
+
17
+ - 每个风格目录:`style.json`(元数据)+ `STYLE.md`(不变量)+ `tokens.json` + `theme.py` + `preview.jpg`;
18
+ - `STYLE.md` 是**改编**,必须在上游基础上写清楚差异;不复制 demo 代码;
19
+ - 颜色、字体、时间表、音频以 `tokens.json` 为准;版面坐标与组件配方写在 `STYLE.md`;
20
+ - 路由:`styles/registry.json` 是元数据,`node scripts/route-style.mjs "<题材>"` 打分;点名优先,低置信度/并列先问用户;
21
+ - 新片在自己的项目目录写 `DESIGN.md`,头部 `style: <slug>` + `style_version: 1.0.0`;
22
+ - 参考与示例统一用 HyperFrames 的 [examples](https://hyperframes.heygen.com/examples) / [catalog](https://hyperframes.heygen.com/catalog),不在仓库里放样片(`showcase/` 只放可复现的演示)。
23
+
24
+ ## 许可
25
+
26
+ - 上游风格文档:Lemo-Opuscar / subinium / adithya-s-k 均为 MIT,分别署名;
27
+ - 上游 demo 静帧:CC BY 4.0,商用需署名;
28
+ - 本仓库的 `theme.py` / `tokens.json` / 场景代码:MIT。
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: blueprint
3
+ version: 1.0.0
4
+ source: https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/blueprint
5
+ license: MIT(上游风格文档);demo 静帧 CC BY 4.0(LemoLab)
6
+ status: adapted
7
+ ---
8
+
9
+ # Blueprint · 蓝图 / 工程制图
10
+
11
+ > 上游:Lemo-Opuscar `styles/blueprint`(MIT)。保留青晒纸、白线、制图顺序与一次红色印章;
12
+ > 去掉了 WebGL 纸纹与 3D 投影。
13
+
14
+ ## 1. 定位
15
+
16
+ 工程图在青晒纸上活起来:普鲁士蓝上的白色技术线,按制图顺序画出,标注会动。
17
+
18
+ - 适合:几何证明、工程结构、尺寸关系、专利/发明叙事;
19
+ - 不适合:需要真实材质、角色表演、实拍的题材。
20
+
21
+ ## 2. 不变量
22
+
23
+ | 项 | 规则 |
24
+ |---|---|
25
+ | 桌 | 深色绘图桌 `#0D1116` |
26
+ | 纸 | 青晒蓝 `#18336B → #2A58A6`,可有轻微不均 |
27
+ | 线 | 白 `#E7F0F9`;四种线宽(轮廓/细节/细/发丝);虚线有制图含义 |
28
+ | 印章 | 全片只有一个非蓝颜色:红 `#B5442E`,只盖一次 |
29
+ | 字体 | 手写体注释(Chalkboard SE);技术字段(Menlo);公式 New Computer Modern |
30
+ | 运动 | draw-on:中心线 → 轮廓 → 细节 → 尺寸 → 标注;不做 3D 旋转、不做辉光 |
31
+ | 声音 | 纸张、钢笔、尺规、机械;−14 LUFS |
32
+
33
+ ## 3. manimgx 映射
34
+
35
+ - `theme.py`:`apply_config()`、`backdrop()`(青晒纸渐变 + 噪点)、`sheet()`、`title_block()`;
36
+ - 用 `Line`、`Polygon`、`DoubleArrow`、`Create` 实现制图与 draw-on;
37
+ - 上游的 3D 斜投影、纸纹 shader 没有实现;用 2D 制图语言近似;
38
+ - 字体替换:Architects Daughter → Chalkboard SE;B612 → Menlo。
39
+
40
+ ## 4. Do / Don't
41
+
42
+ **Do**
43
+
44
+ - 按制图顺序画:先中心线,再轮廓,最后尺寸与标注;
45
+ - 红色印章只出现一次,承担结论;
46
+ - 尺寸线、引出线、编号气球让画面“像图”;
47
+ - 标题块写图号/状态。
48
+
49
+ **Don't**
50
+
51
+ - 不做 3D 渲染、辉光、全息 HUD;
52
+ - 不用第二个非蓝颜色;
53
+ - 不让主线贴边;文字不压线。
54
+
55
+ ## 5. 与上游的差异
56
+
57
+ - 上游是 WebGL2 纸纹 + 3D 斜投影;这里是 2D 平涂 + 渐变;
58
+ - 上游 24 fps、30–60 秒;本实现 30 fps、7 秒量级;
59
+ - 上游手写字体不随包分发;这里用 Chalkboard SE / Menlo 近似。
Binary file
@@ -0,0 +1,47 @@
1
+ {
2
+ "slug": "blueprint",
3
+ "en": "Blueprint",
4
+ "cn": "蓝图工程制图",
5
+ "category_en": "Graphic & Type",
6
+ "category_cn": "图形与排版",
7
+ "uses": [
8
+ "Geometry proof",
9
+ "Structures",
10
+ "Dimensions"
11
+ ],
12
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/blueprint",
13
+ "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
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"
47
+ }
@@ -0,0 +1,65 @@
1
+ """blueprint 1.0.0 — Lemo-Opuscar 的 Blueprint,manimgx 实现层。
2
+
3
+ 上游规范(MIT):styles/blueprint/STYLE.md
4
+ 普鲁士蓝纸 + 白线 + 一次红色印章;不做 3D、不做辉光。
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
+ TABLE = TOKENS["colors"]["table"]
19
+ PAPER = TOKENS["colors"]["paper"]
20
+ PAPER_LIGHT = TOKENS["colors"]["paper_light"]
21
+ LINE = TOKENS["colors"]["line"]
22
+ MUTED = TOKENS["colors"]["muted"]
23
+ STAMP = TOKENS["colors"]["stamp"]
24
+ LAYOUT = TOKENS["layout"]
25
+
26
+
27
+ def apply_config(width: int = 1920, height: int = 1080, fps: int = 30) -> None:
28
+ m.config.pixel_width = width
29
+ m.config.pixel_height = height
30
+ m.config.frame_rate = fps
31
+ m.config.background_color = TABLE
32
+
33
+
34
+ def backdrop(seed: int = 11) -> m.ImageMobject:
35
+ """青晒纸:两档普鲁士蓝 + 轻微噪点,压在深色绘图桌上。"""
36
+ w, h = m.config.pixel_width, m.config.pixel_height
37
+ yy, xx = np.mgrid[0:h, 0:w]
38
+ u = xx / (w - 1)
39
+ v = yy / (h - 1)
40
+ glow = np.exp(-(((u - 0.48) ** 2) / 0.16 + ((v - 0.44) ** 2) / 0.10))
41
+ dark = np.array([24, 51, 107], dtype=float)
42
+ light = np.array([42, 88, 166], dtype=float)
43
+ rgb = dark + glow[..., None] * (light - dark)
44
+ rng = np.random.default_rng(seed)
45
+ rgb = np.clip(rgb + rng.normal(0, 2.1, rgb.shape), 0, 255).astype(np.uint8)
46
+ img = m.ImageMobject(rgb, scale_to_resolution=min(w, h), resampling_algorithm=3)
47
+ return img.move_to(m.ORIGIN)
48
+
49
+
50
+ def sheet() -> m.Rectangle:
51
+ """纸边:一条细白线,标出图幅。"""
52
+ return m.Rectangle(
53
+ width=LAYOUT["frame"]["width"] - 0.9,
54
+ height=LAYOUT["frame"]["height"] - 0.9,
55
+ color=LINE,
56
+ stroke_width=1.2,
57
+ )
58
+
59
+
60
+ def title_block() -> m.VGroup:
61
+ x, y = LAYOUT["title_block"]["x"], LAYOUT["title_block"]["y"]
62
+ box = m.Rectangle(width=3.3, height=1.15, color=LINE, stroke_width=1.0).move_to([x, y, 0])
63
+ name = m.Text("DRAWING 001", font="Menlo", font_size=21, color=LINE).move_to([x - 0.35, y + 0.27, 0])
64
+ status = m.Text("STATUS: VERIFIED", font="Menlo", font_size=20, color=MUTED).move_to([x - 0.05, y - 0.27, 0])
65
+ return m.VGroup(box, name, status)
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "blueprint",
3
+ "version": "1.0.0",
4
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/blueprint",
5
+ "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
6
+ "colors": {
7
+ "table": "#0D1116",
8
+ "paper": "#18336B",
9
+ "paper_light": "#2A58A6",
10
+ "line": "#E7F0F9",
11
+ "muted": "#9FB6D4",
12
+ "stamp": "#B5442E"
13
+ },
14
+ "typography": {
15
+ "hand": { "family": "Chalkboard SE", "size": 24 },
16
+ "tech": { "family": "Menlo", "size": 17 },
17
+ "math": { "family": "New Computer Modern", "size": 54 }
18
+ },
19
+ "layout": {
20
+ "aspect": "16:9",
21
+ "frame": { "width": 14.222, "height": 8.0 },
22
+ "margin": 0.55,
23
+ "title_block": { "x": 4.95, "y": -2.95 }
24
+ },
25
+ "motion": { "fps": 30, "easing": "draw-on", "hold_min_seconds": 1.0 },
26
+ "audio": { "target_lufs": -14, "true_peak_db": -1 }
27
+ }
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: dataviz
3
+ version: 1.0.0
4
+ source: https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/dataviz
5
+ license: MIT(上游风格文档);demo 静帧 CC BY 4.0(LemoLab)
6
+ status: adapted
7
+ ---
8
+
9
+ # Data Storytelling · 数据叙事
10
+
11
+ > 上游:Lemo-Opuscar `styles/dataviz`(MIT)。保留“图表就是电影”的语法;
12
+ > 铅笔/纸纹等材质实现换成了 manimgx 的平涂与细线。
13
+
14
+ ## 1. 定位
15
+
16
+ 一张会讲故事的图:真实数据、逐笔画出、手写注释钉在数字上。图表本身就是镜头、节奏和情节。
17
+
18
+ - 适合:趋势、比较、分布、前后变化、科学数据;
19
+ - 不适合:没有数据支撑的口号、纯角色故事。
20
+
21
+ ## 2. 不变量
22
+
23
+ | 项 | 规则 |
24
+ |---|---|
25
+ | 纸 | 奶油纸 `#F4EDE1`;墨 `#2B2622`;网格 `#C9BFAE` |
26
+ | 数据色 | 发散数据用 RdBu(`#2166AC → #F7F7F7 → #B2182B`),以有意义的均值为中心 |
27
+ | 强调 | 蓝 `#2F5D8A` 表示过程/记忆;红 `#B5442E` 表示记录/警告/结论 |
28
+ | 字体 | 衬线标题与图注(Songti SC);等宽刻度(Menlo);公式 New Computer Modern |
29
+ | 运动 | 图表操作就是转场:轴生长、点落下、尺度变化、形变;不用淡入淡出 |
30
+ | 注释 | 钉在数据空间(值 + 像素偏移),缩放后仍跟着点 |
31
+ | 声音 | 每个 mark 一个音高;−14 LUFS |
32
+
33
+ ## 3. manimgx 映射
34
+
35
+ - `theme.py`:`apply_config()`、`backdrop()`、`rule()`、`leader()`;
36
+ - 用 `Axes`、`Dot`、`Line`、`always_redraw` 实现“轴生长 / 点落下 / 割线滑动”;
37
+ - 上游的铅笔、纸纹、手写抖动没有实现;用颜色与注释层级表达同一语法;
38
+ - 字体替换:Newsreader → Songti SC;IBM Plex Mono → Menlo。
39
+
40
+ ## 4. Do / Don't
41
+
42
+ **Do**
43
+
44
+ - 一次只讲一个想法;让轴、标注、尺度变化承担叙事;
45
+ - 注释贴着数据点,颜色有语义(蓝=过程,红=结论);
46
+ - 结论出现时停住,给读者读数时间。
47
+
48
+ **Don't**
49
+
50
+ - 不做 dashboard;不堆多个图;
51
+ - 不为了戏剧性拉伸色阶;
52
+ - 不用装饰性图标代替数据。
53
+
54
+ ## 5. 与上游的差异
55
+
56
+ - 上游是 Canvas 2D + 手写笔刷;这里是矢量平涂,质感更干净;
57
+ - 上游 24 fps、50 秒量级;本实现 30 fps、10 秒量级;
58
+ - ColorBrewer RdBu 为标准色板;纸色/墨色取上游描述的中值,验收以帧为准。
Binary file
@@ -0,0 +1,50 @@
1
+ {
2
+ "slug": "dataviz",
3
+ "en": "Data Storytelling",
4
+ "cn": "数据叙事",
5
+ "category_en": "Information & Keynote",
6
+ "category_cn": "信息与发布",
7
+ "uses": [
8
+ "Trends",
9
+ "Comparison",
10
+ "Distribution",
11
+ "Scientific data"
12
+ ],
13
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/dataviz",
14
+ "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
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"
50
+ }
@@ -0,0 +1,49 @@
1
+ """dataviz 1.0.0 — Lemo-Opuscar 的 Data Storytelling,manimgx 实现层。
2
+
3
+ 上游规范(MIT):styles/dataviz/STYLE.md
4
+ 纸、墨、蓝红铅笔;颜色与字号只从 tokens.json 取。
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
+ PAPER = TOKENS["colors"]["paper"]
17
+ PAPER_SHADOW = TOKENS["colors"]["paper_shadow"]
18
+ INK = TOKENS["colors"]["ink"]
19
+ GRID = TOKENS["colors"]["grid"]
20
+ MUTED = TOKENS["colors"]["muted"]
21
+ BLUE = TOKENS["colors"]["blue"]
22
+ RED = TOKENS["colors"]["red"]
23
+ FONT = TOKENS["typography"]["display"]["family"]
24
+ MONO = TOKENS["typography"]["mono"]["family"]
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 = PAPER
33
+
34
+
35
+ def backdrop() -> m.Rectangle:
36
+ """一张平整的奶油纸;不要渐变、不要投影。"""
37
+ return (
38
+ m.Rectangle(width=LAYOUT["frame"]["width"], height=LAYOUT["frame"]["height"], color=PAPER)
39
+ .set_fill(PAPER, 1)
40
+ .set_stroke(width=0)
41
+ )
42
+
43
+
44
+ def rule(y: float, width: float, x: float = 0.0, color: str = GRID, stroke_width: float = 1.0) -> m.Line:
45
+ return m.Line([x - width / 2, y, 0], [x + width / 2, y, 0], color=color, stroke_width=stroke_width)
46
+
47
+
48
+ def leader(start, end, color: str = MUTED, stroke_width: float = 1.0) -> m.DashedLine:
49
+ return m.DashedLine(start, end, color=color, stroke_width=stroke_width, dash_length=0.08)
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "dataviz",
3
+ "version": "1.0.0",
4
+ "source": "https://github.com/lemomo-ai/lemo-opuscar/tree/main/styles/dataviz",
5
+ "license": "style doc MIT; demo stills CC BY 4.0 (LemoLab)",
6
+ "colors": {
7
+ "paper": "#F4EDE1",
8
+ "paper_shadow": "#E8DECF",
9
+ "ink": "#2B2622",
10
+ "grid": "#C9BFAE",
11
+ "muted": "#7A6F63",
12
+ "blue": "#2F5D8A",
13
+ "red": "#B5442E",
14
+ "ramp_low": "#2166AC",
15
+ "ramp_mid": "#F7F7F7",
16
+ "ramp_high": "#B2182B"
17
+ },
18
+ "typography": {
19
+ "display": { "family": "Songti SC", "weight": "BOLD", "size": 42 },
20
+ "body": { "family": "Songti SC", "size": 24 },
21
+ "mono": { "family": "Menlo", "size": 18 },
22
+ "math": { "family": "New Computer Modern", "size": 56 }
23
+ },
24
+ "layout": {
25
+ "aspect": "16:9",
26
+ "frame": { "width": 14.222, "height": 8.0 },
27
+ "margin": 0.55
28
+ },
29
+ "motion": { "fps": 30, "easing": "chart-operation", "hold_min_seconds": 1.2 },
30
+ "audio": { "target_lufs": -14, "true_peak_db": -1 }
31
+ }