manim-simplex 0.2.0__py3-none-any.whl
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.
- manim_simplex-0.2.0.dist-info/METADATA +214 -0
- manim_simplex-0.2.0.dist-info/RECORD +39 -0
- manim_simplex-0.2.0.dist-info/WHEEL +4 -0
- manim_simplex-0.2.0.dist-info/entry_points.txt +2 -0
- manim_simplex-0.2.0.dist-info/licenses/LICENSE +21 -0
- simplex/engine/README.md +38 -0
- simplex/engine/__init__.py +31 -0
- simplex/engine/animations.py +160 -0
- simplex/engine/code.py +442 -0
- simplex/engine/debug.py +181 -0
- simplex/engine/defaults.py +33 -0
- simplex/engine/dynamics.py +100 -0
- simplex/engine/geometry.py +247 -0
- simplex/engine/ghost_fade.py +72 -0
- simplex/engine/glyph_map.py +339 -0
- simplex/engine/region.py +304 -0
- simplex/engine/scaling.py +77 -0
- simplex/engine/text.py +110 -0
- simplex/manifest.py +114 -0
- simplex/mobjects/README.md +33 -0
- simplex/mobjects/__init__.py +13 -0
- simplex/mobjects/array.py +408 -0
- simplex/mobjects/graph.py +59 -0
- simplex/mobjects/outline.py +141 -0
- simplex/plugin.py +74 -0
- simplex/py.typed +0 -0
- simplex/section.py +51 -0
- simplex/slides/README.md +43 -0
- simplex/slides/__init__.py +12 -0
- simplex/slides/base.py +118 -0
- simplex/slides/chrome.py +69 -0
- simplex/slides/outline.py +433 -0
- simplex/theme/README.md +19 -0
- simplex/theme/__init__.py +28 -0
- simplex/theme/context.py +29 -0
- simplex/theme/presets.py +87 -0
- simplex/theme/pygments_style.py +78 -0
- simplex/theme/tokens.py +105 -0
- simplex/theme/web_css.py +33 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: manim-simplex
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Manim plugin: theme tokens, mobjects, slide hierarchy, deck manifest schema.
|
|
5
|
+
Project-URL: Changelog, https://github.com/shlomi-perles/manim-simplex/blob/main/CHANGELOG.md
|
|
6
|
+
Project-URL: Homepage, https://github.com/shlomi-perles/manim-simplex
|
|
7
|
+
Project-URL: Issues, https://github.com/shlomi-perles/manim-simplex/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/shlomi-perles/manim-simplex
|
|
9
|
+
Author: Shlomi Perles
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: animation,computer-science,education,lecture,manim,manim-slides,math,presentation
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Education
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Education
|
|
22
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
23
|
+
Classifier: Topic :: Multimedia :: Video
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.13
|
|
27
|
+
Requires-Dist: manim-slides>=5.1.7
|
|
28
|
+
Requires-Dist: manim>=0.20.1
|
|
29
|
+
Requires-Dist: pydantic>=2.7
|
|
30
|
+
Requires-Dist: pygments>=2.18
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# manim-simplex
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/manim-simplex/)
|
|
36
|
+
[](https://pypi.org/project/manim-simplex/)
|
|
37
|
+
[](https://github.com/shlomi-perles/manim-simplex/blob/main/LICENSE)
|
|
38
|
+
|
|
39
|
+
The Manim plugin half of [Simplex](https://github.com/shlomi-perles/simplex):
|
|
40
|
+
theme tokens, reusable mobjects, slide hierarchy, deck manifest schema,
|
|
41
|
+
and the `manim.plugins` entry-point. Distributed on PyPI as
|
|
42
|
+
`manim-simplex`.
|
|
43
|
+
|
|
44
|
+
The lecture-portal platform (CLI, deck discovery, render orchestration,
|
|
45
|
+
web builder) lives in the sibling [`simplex`](https://github.com/shlomi-perles/simplex)
|
|
46
|
+
package; both contribute modules to the shared PEP 420 `simplex/`
|
|
47
|
+
namespace.
|
|
48
|
+
|
|
49
|
+
## What ships here
|
|
50
|
+
|
|
51
|
+
| Module | Contents |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `simplex.plugin` | `activate()` -- the `manim.plugins` entry-point. Applies the active theme to `manim.config`. |
|
|
54
|
+
| `simplex.section` | `SimplexSectionType` enum -- the slide-hierarchy strings written into Manim's sections JSON. Manim-free. |
|
|
55
|
+
| `simplex.manifest` | `DeckManifest`, `MainSlide`, `Subsection` Pydantic models -- the cross-package contract consumed by the `simplex` web builder. Manim-free. |
|
|
56
|
+
| `simplex.theme` | `Theme`, `Palette`, `Typography`, `Spacing`, `Motion`, `LatexProfile`, `WebPalette`, `active_theme`, `get_active_theme`, `presets`, `render_web_css`. |
|
|
57
|
+
| `simplex.engine` | Animation primitives -- `Region`, `Remove`, `clear_scene`, `exit_for`, `register_exit`, `set_exit_animation`, `HighlightResult`, `apply_theme_defaults`, plus the `glyph_map`, `ghost_fade`, `dynamics`, `geometry`, `code`, `text`, `scaling`, `debug` submodules. |
|
|
58
|
+
| `simplex.mobjects` | `Node`, `Edge`, `ArrayMob`, `ArrayEntry`, `ArrayPointer`, `OutlineProgressBar`. |
|
|
59
|
+
| `simplex.slides` | `BaseSlide`, `OutlineScene`, `OutlinePart`, `Chrome`, `make_chrome`. |
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
uv add manim-simplex
|
|
65
|
+
# or
|
|
66
|
+
pip install manim-simplex
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
System dependencies (texlive, ffmpeg, cairo, pango) are the same as
|
|
70
|
+
Manim's -- see the [Manim install guide](https://docs.manim.community/en/stable/installation.html).
|
|
71
|
+
|
|
72
|
+
## Quick start
|
|
73
|
+
|
|
74
|
+
```ini
|
|
75
|
+
# decks/<your-deck>/manim.cfg
|
|
76
|
+
[CLI]
|
|
77
|
+
plugins = simplex
|
|
78
|
+
save_sections = True
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from manim import MathTex
|
|
83
|
+
from simplex.slides import BaseSlide, make_chrome
|
|
84
|
+
from simplex.theme import presets
|
|
85
|
+
|
|
86
|
+
class Hello(BaseSlide):
|
|
87
|
+
def setup(self) -> None:
|
|
88
|
+
super().setup()
|
|
89
|
+
chrome = make_chrome(presets.DASTIMATOR_DARK, self.region, header="Hello")
|
|
90
|
+
self.add_to_canvas(**chrome.mobjects)
|
|
91
|
+
self.region = chrome.body_region
|
|
92
|
+
|
|
93
|
+
def construct(self) -> None:
|
|
94
|
+
from manim import ORIGIN, Write
|
|
95
|
+
eq = MathTex(r"e^{i\pi} + 1 = 0")
|
|
96
|
+
self.region.place(eq, ORIGIN)
|
|
97
|
+
self.play(Write(eq))
|
|
98
|
+
self.next_slide(name="Hello")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv run manim-slides render path/to/your_deck/scene.py Hello
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Slide hierarchy
|
|
106
|
+
|
|
107
|
+
`BaseSlide.next_slide` writes a `SimplexSectionType` value into Manim's
|
|
108
|
+
native section JSON. The web builder reconciles that with manim-slides'
|
|
109
|
+
`PresentationConfig` to build a main/sub tree.
|
|
110
|
+
|
|
111
|
+
- `self.next_slide(name="Title")` -> **MAIN** slide named `"Title"`.
|
|
112
|
+
- `self.next_slide()` as the *first* call -> **MAIN** slide
|
|
113
|
+
auto-named after the scene class with PascalCase boundaries spaced
|
|
114
|
+
out (``DFSLecture`` → ``"DFS Lecture"``; no warning).
|
|
115
|
+
- `self.next_slide()` after a named main -> **SUB** slide.
|
|
116
|
+
- `self.next_slide(..., loop=True)` -> the `LOOP` variant.
|
|
117
|
+
- `self.next_slide(..., section_type="simplex.main.skip")` -> explicit
|
|
118
|
+
override always wins.
|
|
119
|
+
|
|
120
|
+
## Outline slides
|
|
121
|
+
|
|
122
|
+
`OutlineScene` composes typed `OutlinePart` objects into an animated
|
|
123
|
+
`BaseSlide` outline. Each part owns already-built Manim mobjects for its
|
|
124
|
+
feature title, compact label, and optional visual. Progress dots are
|
|
125
|
+
positioned with `self.region.linspace(RIGHT, n)` defaults, so edge
|
|
126
|
+
margins and inter-dot gaps are equal.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from manim import Circle, Square, Tex
|
|
130
|
+
from simplex.engine.text import Caption
|
|
131
|
+
from simplex.slides import OutlinePart, OutlineScene
|
|
132
|
+
|
|
133
|
+
class Outline(OutlineScene):
|
|
134
|
+
def __init__(self, **kwargs):
|
|
135
|
+
super().__init__(
|
|
136
|
+
parts=[
|
|
137
|
+
OutlinePart(Tex("Research Question"), Caption("Question"), Circle()),
|
|
138
|
+
OutlinePart(Tex("Algorithms"), Caption("Algorithms"), Square()),
|
|
139
|
+
],
|
|
140
|
+
**kwargs,
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Theme
|
|
145
|
+
|
|
146
|
+
Themes are frozen Pydantic models -- the same instance produces:
|
|
147
|
+
|
|
148
|
+
1. Manim defaults (via `apply_theme_defaults`, called by the plugin).
|
|
149
|
+
2. A `TexTemplate` (via `LatexProfile.as_tex_template`).
|
|
150
|
+
3. CSS variables for the web portal + RevealJS HTML (via
|
|
151
|
+
`render_web_css(theme.web_palette)`).
|
|
152
|
+
4. The `darcula` Pygments style (registered by the plugin).
|
|
153
|
+
|
|
154
|
+
Switch themes per-scope with `active_theme`:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from simplex.theme import presets
|
|
158
|
+
from simplex.theme.context import active_theme
|
|
159
|
+
|
|
160
|
+
from manim import Tex
|
|
161
|
+
|
|
162
|
+
with active_theme(presets.ACADEMIC_LIGHT):
|
|
163
|
+
label = Tex("This Tex picks up the academic light palette.")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Cross-package contract
|
|
167
|
+
|
|
168
|
+
`manim-simplex` owns the manifest schema; `simplex` imports it:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from simplex.manifest import DeckManifest, MainSlide, Subsection
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
When the schema bumps `schema_version`, the web builder hard-fails on
|
|
175
|
+
unknown versions with a pointer at the `manim-simplex` upgrade. This
|
|
176
|
+
keeps the two repos honest about their contract.
|
|
177
|
+
|
|
178
|
+
## Why a separate distribution?
|
|
179
|
+
|
|
180
|
+
The plugin surface (mobjects + theme + entry-point + manifest schema)
|
|
181
|
+
is reusable independently of the lecture-portal pipeline. Splitting
|
|
182
|
+
them lets the plugin be a thin dependency for users who want to render
|
|
183
|
+
slides without pulling in Typer, watchfiles, Jinja, and the web
|
|
184
|
+
builder stack.
|
|
185
|
+
|
|
186
|
+
Python's PEP 420 implicit namespace packages merge the two distributions
|
|
187
|
+
at import time. Neither wheel ships `src/simplex/__init__.py`, so
|
|
188
|
+
`from simplex.engine import Remove` resolves regardless of which wheel
|
|
189
|
+
contributed the module.
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/).
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
git clone https://github.com/shlomi-perles/manim-simplex.git
|
|
197
|
+
cd manim-simplex
|
|
198
|
+
uv sync --all-extras
|
|
199
|
+
uv run pre-commit install
|
|
200
|
+
uv run pytest -q
|
|
201
|
+
uv run ruff check .
|
|
202
|
+
uv run basedpyright
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Examples under `examples/` are runnable demo scenes; they double as
|
|
206
|
+
documentation and CI smoke tests:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
uv run manim -pql examples/hello_slide.py HelloSlide
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
simplex/manifest.py,sha256=g7pQoTJb0fD_FGvvXFmNkwMrNDR5mPTWm8GD4etEZzs,3646
|
|
2
|
+
simplex/plugin.py,sha256=aIMQhTgOAv8gv-ZvPTNPJ-NMM3W1OtOC8lvLZetLfF4,2990
|
|
3
|
+
simplex/section.py,sha256=tJfR5rVPAG-Wud0dM-wNeCexfDgRtu4fupGX-ojEFs4,1654
|
|
4
|
+
simplex/engine/README.md,sha256=rHMhErOWc9X6aq6k75I1aVjIyhjNFvrL9LSiYiTfCDk,2991
|
|
5
|
+
simplex/engine/__init__.py,sha256=WyGyuduPx1c3ESuDkHPs78JqXIRBavGYJcFV0_EuTVg,808
|
|
6
|
+
simplex/engine/animations.py,sha256=X-y1hA9o9woRJIPfMw3zqSS0ePn5sKCJtJwfjmuCNM0,5565
|
|
7
|
+
simplex/engine/code.py,sha256=t-ejp9aQrey6HD_eufx1xqD-hKstvSBIn0Mg7VCOH3o,15134
|
|
8
|
+
simplex/engine/debug.py,sha256=18u4udhT4XD8Sa7dXY9jxpz7jzkA08IvsQglEGTP2Mg,5636
|
|
9
|
+
simplex/engine/defaults.py,sha256=ZG8I0FUMwOnpe0QmB4m_vXFOSG_FuCTLOJA6S4eLVlE,1093
|
|
10
|
+
simplex/engine/dynamics.py,sha256=kxjlvGb00EKiYS0_lKvxeCjCAHSvEDKkGfkUIbrf1mc,3403
|
|
11
|
+
simplex/engine/geometry.py,sha256=kjbR3DRSV1rHlGMkRGM8OF38Qhw0M4odKjQGj_zAEdQ,8279
|
|
12
|
+
simplex/engine/ghost_fade.py,sha256=s3EG-Ope47gOY4miSfjdB7W-6zSIy4EiojKh1Uey6_c,2823
|
|
13
|
+
simplex/engine/glyph_map.py,sha256=Fz5SfapdC6bfRoZwZ4hWvraGYImUtmQ6h14VDjxsOnQ,12820
|
|
14
|
+
simplex/engine/region.py,sha256=qZvmSQhAchLEklnLwxrmpRF2_PDwH5uKWGcI49dVkGo,10821
|
|
15
|
+
simplex/engine/scaling.py,sha256=_7chCXatOA8WIm2w9qDbJNgdkyWZ-F9hK4oa4LmEmag,2456
|
|
16
|
+
simplex/engine/text.py,sha256=3O3TuNLiOCX5-Wgjx9gRRoBMFuc-UilnvsZqdBukb7w,3909
|
|
17
|
+
simplex/mobjects/README.md,sha256=_vOhB65LibwkV276SCbSwj3JPhgNpBUmMawSJhro-Wg,1168
|
|
18
|
+
simplex/mobjects/__init__.py,sha256=crKQGZN2ZwEH3kE5dAszsrVcruYkZScK6l7usnd2XFE,531
|
|
19
|
+
simplex/mobjects/array.py,sha256=C02Vavj8c7rK2Pa0HchP354MGwPAWi2Mir8Nuiovc2A,13700
|
|
20
|
+
simplex/mobjects/graph.py,sha256=HtuDYESlt7aJgCu0KbLy5NKqk81-Ow60b6Y-d_8DkIA,1983
|
|
21
|
+
simplex/mobjects/outline.py,sha256=3uQiyT6NCaDBx9jLu6vc7hl7yDYUNQhxKlVIKarMhiU,5065
|
|
22
|
+
simplex/slides/README.md,sha256=h2XjoR3FqwXDS43dFtBakUKwloUKWpqDlUVDFL-njrc,2018
|
|
23
|
+
simplex/slides/__init__.py,sha256=rbcsHN8BlImuEjpXzFPsAADX9dCoDXvdDhVzIgTAy8w,469
|
|
24
|
+
simplex/slides/base.py,sha256=9bpOtIdLhe3FYiE2pJo3XVdPCXbH1XRk6LkDmmYBM_U,4488
|
|
25
|
+
simplex/slides/chrome.py,sha256=PACBPP7IZ40RBoE6aXHLxGwSAX0RpkwnS64zjAt3ATw,2391
|
|
26
|
+
simplex/slides/outline.py,sha256=PPoAMKRCLB38MF65UWryFivRW-M-A3RFuIIxCkzZh0c,15380
|
|
27
|
+
simplex/theme/README.md,sha256=Ie94AOM3dC9dVdCSZo3eTXtVN8n_aRTqwIbqOtpetWQ,900
|
|
28
|
+
simplex/theme/__init__.py,sha256=nK8kxrSLAGsvciEs0wrVmmrfLgAr5nyk34PdQsNYjBI,540
|
|
29
|
+
simplex/theme/context.py,sha256=vDY_CtUQ6SxdnmGZFdHJ5X4vrnvbl4p8Pm53KRSpom0,787
|
|
30
|
+
simplex/theme/presets.py,sha256=pRT6S57coBUSyMQcZ4lTsMtmBfz66A81TPUV_crVQUo,2429
|
|
31
|
+
simplex/theme/pygments_style.py,sha256=sHkdMhwelJzZHpFDtOwy87El2SC2gUg7jp34Wc3SaGU,2789
|
|
32
|
+
simplex/theme/tokens.py,sha256=2UjMGE927gVAC7dcV51Qlb9s0FUCUyPY36TTZ-lATc8,3073
|
|
33
|
+
simplex/theme/web_css.py,sha256=VQURjkh038Iwu6_tt8wkfNVUD4aYcenvio3uHY1FsKs,1218
|
|
34
|
+
simplex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
+
manim_simplex-0.2.0.dist-info/METADATA,sha256=VqT3nJpEpkqxTfcoZKIAmBKusme0vbwuPVOJf-N__lo,7857
|
|
36
|
+
manim_simplex-0.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
37
|
+
manim_simplex-0.2.0.dist-info/entry_points.txt,sha256=mEZB63zKWkjk7pWQSE9cwsO6msA9DizJwa_TV9OXnQU,50
|
|
38
|
+
manim_simplex-0.2.0.dist-info/licenses/LICENSE,sha256=nl59bQ87NqHIcm_46xQjWvu0Nv630wsBOK4I1-ou9SY,1070
|
|
39
|
+
manim_simplex-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shlomi Perles
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
simplex/engine/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# engine/
|
|
2
|
+
|
|
3
|
+
Small additive helpers that augment vanilla Manim. **Never wrap Manim's constructors.**
|
|
4
|
+
|
|
5
|
+
## Public surface
|
|
6
|
+
|
|
7
|
+
- `apply_theme_defaults(theme)` -- calls `Mobject.set_default(...)` for Tex / MathTex / Text / Line / Dot / Arrow / Rectangle / Square (invoked by `simplex.plugin:activate`)
|
|
8
|
+
- `Region` -- mutable rectangular drawing area; default lives on `BaseSlide.region`
|
|
9
|
+
- `Remove(mob, **kw)` -- exit animation lookup; dispatches through `exit_for(mob)`
|
|
10
|
+
- `exit_for(mob, **kw)` -- per-instance override (WeakKeyDictionary) -> MRO match in defaults -> `FadeOut`
|
|
11
|
+
- `register_exit(mob_type, factory)` -- register a default exit for a Mobject class
|
|
12
|
+
- `set_exit_animation(mob, factory)` -- per-instance exit override (stored in a `WeakKeyDictionary`; no monkey-patching)
|
|
13
|
+
- `clear_scene(scene, *, exclude=())` -- free function used by `BaseSlide.clear_scene`
|
|
14
|
+
- `HighlightResult` -- typed return for `highlight_code_lines` (fade + optional indicate, iterable)
|
|
15
|
+
|
|
16
|
+
Cross-package types live one level up:
|
|
17
|
+
|
|
18
|
+
- `simplex.section.SimplexSectionType` -- enum encoded into Manim's section JSON
|
|
19
|
+
- `simplex.manifest.DeckManifest` / `MainSlide` / `Subsection` -- web builder contract
|
|
20
|
+
|
|
21
|
+
## Submodules (import directly to keep `simplex.engine` cheap)
|
|
22
|
+
|
|
23
|
+
- `engine.text` -- `Caption`, `TexPage` (fixed-width minipage; `width_cm` kwarg / class attr); `color_tex(eq, t2c)`; `search_shape_in_text`. Body-sized paragraphs use plain `manim.Tex` -- `apply_theme_defaults` already sets `font_size=theme.typography.body`.
|
|
24
|
+
- `engine.code` -- `code_block`, `highlight_code_lines`, `code_explain`, `transform_code_lines`; `DarculaStyle`, `register_darcula`; `HighlightResult`
|
|
25
|
+
- `engine.geometry` -- `get_convex_hull_polygon`, `get_surrounding_rectangle`, `get_frame_center`, `Vcis`, `Arc3d`, `SurroundingRectangleUnion`
|
|
26
|
+
- `engine.glyph_map` -- `TransformByGlyphMap` (glyph-indexed Tex transitions)
|
|
27
|
+
- `engine.ghost_fade` -- `GhostSlideFade` (one-shot fade-in/drift/fade-out cue)
|
|
28
|
+
- `engine.dynamics` -- `VT` (`~`/`@`/`@=` over `ValueTracker`), `DN` (auto-tracking `DecimalNumber`), `keep_orientation`, `maintain_apparent_stroke_width`
|
|
29
|
+
- `engine.scaling` -- `scale_to_fit` (multi-axis fit + buff), `scale_to_fit_mobject`, `scale_with_stroke_width`
|
|
30
|
+
- `engine.debug` -- `bounding_box`, `indexx_labels` (multi-color), `debug_glyph(s)`
|
|
31
|
+
|
|
32
|
+
## Don't
|
|
33
|
+
|
|
34
|
+
- Don't call `Mobject.set_default(...)` outside `apply_theme_defaults`.
|
|
35
|
+
- Don't subclass Manim Mobjects to inject defaults; use `set_default` via `apply_theme_defaults`.
|
|
36
|
+
- Don't reimplement what Manim ships: `ValueTracker` arithmetic ops, `index_labels`, `ConvexHull`, `Union`, `Polygon.round_corners`, `scale_to_fit_height/_width/_depth`, `BraceLabel`/`BraceText`, `Mobject.always` -- all already in 0.20.x.
|
|
37
|
+
- Don't import Manim at module load time from animations / region / defaults -- import inside the function so importing `simplex.engine` stays cheap.
|
|
38
|
+
- Don't monkey-patch Mobjects (no `_simplex_*` attributes). Use the `WeakKeyDictionary` registry in `animations.py` instead.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Engine helpers that augment vanilla Manim.
|
|
2
|
+
|
|
3
|
+
The engine ships animation primitives, layout helpers, and small custom
|
|
4
|
+
mobjects -- everything that *isn't* a Slide and *isn't* a theme token.
|
|
5
|
+
|
|
6
|
+
Cross-package types like ``SimplexSectionType`` live at the package root
|
|
7
|
+
(``simplex.section``) so the web builder and CLI can import them without
|
|
8
|
+
touching Manim.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from simplex.engine.animations import (
|
|
12
|
+
Remove,
|
|
13
|
+
clear_scene,
|
|
14
|
+
exit_for,
|
|
15
|
+
register_exit,
|
|
16
|
+
set_exit_animation,
|
|
17
|
+
)
|
|
18
|
+
from simplex.engine.code import HighlightResult
|
|
19
|
+
from simplex.engine.defaults import apply_theme_defaults
|
|
20
|
+
from simplex.engine.region import Region
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"HighlightResult",
|
|
24
|
+
"Region",
|
|
25
|
+
"Remove",
|
|
26
|
+
"apply_theme_defaults",
|
|
27
|
+
"clear_scene",
|
|
28
|
+
"exit_for",
|
|
29
|
+
"register_exit",
|
|
30
|
+
"set_exit_animation",
|
|
31
|
+
]
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Default-exit registry, ``Remove``, ``set_exit_animation``, and ``clear_scene``.
|
|
2
|
+
|
|
3
|
+
Each Mobject type has a default *exit* animation (e.g. ``Tex`` -> ``Unwrite``,
|
|
4
|
+
``Circle`` -> ``ShrinkToCenter``). Callers can override per-instance via
|
|
5
|
+
``set_exit_animation(mob, anim_cls_or_factory)``, or per-type via
|
|
6
|
+
``register_exit(type, factory)``.
|
|
7
|
+
|
|
8
|
+
``Remove(mob)`` and ``clear_scene(scene, exclude=...)`` both dispatch through
|
|
9
|
+
``exit_for(mob)``, which checks instance overrides, then walks the MRO of
|
|
10
|
+
``type(mob)`` against the type defaults, falling back to ``FadeOut``.
|
|
11
|
+
|
|
12
|
+
Implementation notes:
|
|
13
|
+
|
|
14
|
+
- The type defaults dict is wrapped in a ``_DefaultRegistry`` singleton with
|
|
15
|
+
a double-checked ``threading.Lock`` around its lazy init. Manim renders are
|
|
16
|
+
mostly single-threaded but plugins can be poked from setup hooks running
|
|
17
|
+
in parallel (e.g. test suites), so the lock is cheap insurance.
|
|
18
|
+
- Per-instance overrides are kept in a ``WeakKeyDictionary`` keyed by the
|
|
19
|
+
Mobject, *not* monkey-patched onto the Mobject as a ``_simplex_exit``
|
|
20
|
+
attribute. Manim's Mobject base is plain-class so both ``__hash__`` (id
|
|
21
|
+
identity) and ``__weakref__`` work; if a downstream subclass disables one
|
|
22
|
+
of these the override call fails fast at ``set_exit_animation`` time
|
|
23
|
+
rather than leaking through a swallowed ``setattr``.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import threading
|
|
27
|
+
from collections.abc import Callable, Iterable
|
|
28
|
+
from typing import Any
|
|
29
|
+
from weakref import WeakKeyDictionary
|
|
30
|
+
|
|
31
|
+
ExitFactory = Callable[..., Any]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _DefaultRegistry:
|
|
35
|
+
"""Type -> exit-factory map with double-checked locked lazy init."""
|
|
36
|
+
|
|
37
|
+
def __init__(self) -> None:
|
|
38
|
+
self._lock = threading.Lock()
|
|
39
|
+
self._map: dict[type, ExitFactory] | None = None
|
|
40
|
+
|
|
41
|
+
def get(self) -> dict[type, ExitFactory]:
|
|
42
|
+
if self._map is None:
|
|
43
|
+
with self._lock:
|
|
44
|
+
if self._map is None:
|
|
45
|
+
self._map = self._build()
|
|
46
|
+
return self._map
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def _build() -> dict[type, ExitFactory]:
|
|
50
|
+
from manim import (
|
|
51
|
+
DOWN,
|
|
52
|
+
Arrow,
|
|
53
|
+
Circle,
|
|
54
|
+
Code,
|
|
55
|
+
DashedLine,
|
|
56
|
+
Dot,
|
|
57
|
+
FadeOut,
|
|
58
|
+
Line,
|
|
59
|
+
MarkupText,
|
|
60
|
+
MathTex,
|
|
61
|
+
ShrinkToCenter,
|
|
62
|
+
Tex,
|
|
63
|
+
Text,
|
|
64
|
+
Uncreate,
|
|
65
|
+
Unwrite,
|
|
66
|
+
VMobject,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def fade_with_drift(m: Any, **kw: Any) -> Any:
|
|
70
|
+
return FadeOut(m, shift=0.1 * DOWN, **kw)
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
Tex: Unwrite,
|
|
74
|
+
MathTex: Unwrite,
|
|
75
|
+
Text: Unwrite,
|
|
76
|
+
MarkupText: Unwrite,
|
|
77
|
+
Code: Unwrite,
|
|
78
|
+
Circle: ShrinkToCenter,
|
|
79
|
+
Dot: ShrinkToCenter,
|
|
80
|
+
Line: Uncreate,
|
|
81
|
+
Arrow: Uncreate,
|
|
82
|
+
DashedLine: Uncreate,
|
|
83
|
+
VMobject: fade_with_drift,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
_REGISTRY = _DefaultRegistry()
|
|
88
|
+
_OVERRIDES: WeakKeyDictionary[Any, ExitFactory] = WeakKeyDictionary()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def register_exit(mob_type: type, factory: ExitFactory) -> None:
|
|
92
|
+
"""Register a default exit animation for ``mob_type`` (and subclasses via MRO).
|
|
93
|
+
|
|
94
|
+
``factory(mob, **kwargs)`` must return an ``Animation``. Most callers pass
|
|
95
|
+
an ``Animation`` class directly (``Unwrite``, ``FadeOut``, ...) since
|
|
96
|
+
classes are callable in this signature.
|
|
97
|
+
"""
|
|
98
|
+
_REGISTRY.get()[mob_type] = factory
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def set_exit_animation(mob: Any, factory: ExitFactory) -> Any:
|
|
102
|
+
"""Stash a per-instance exit factory for ``mob``. Returns ``mob`` for chaining.
|
|
103
|
+
|
|
104
|
+
The override lives in a module-level ``WeakKeyDictionary`` -- it disappears
|
|
105
|
+
automatically when ``mob`` is garbage-collected. No attribute is set on
|
|
106
|
+
the Mobject itself.
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
_OVERRIDES[mob] = factory
|
|
110
|
+
except TypeError as exc: # mob is not hashable or weakref-able
|
|
111
|
+
raise TypeError(
|
|
112
|
+
f"set_exit_animation: {type(mob).__name__} cannot be used as a "
|
|
113
|
+
"registry key (no __weakref__ or unhashable). Wrap the mobject "
|
|
114
|
+
"in a VGroup or pass a different mobject."
|
|
115
|
+
) from exc
|
|
116
|
+
return mob
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def exit_for(mob: Any, **kwargs: Any) -> Any:
|
|
120
|
+
"""Return an ``Animation`` that exits ``mob``.
|
|
121
|
+
|
|
122
|
+
Resolution order:
|
|
123
|
+
|
|
124
|
+
1. per-instance override registered by ``set_exit_animation``;
|
|
125
|
+
2. exact type or MRO match in the default registry;
|
|
126
|
+
3. ``FadeOut`` fallback.
|
|
127
|
+
|
|
128
|
+
Any ``**kwargs`` are forwarded to the resolved factory.
|
|
129
|
+
"""
|
|
130
|
+
override = _OVERRIDES.get(mob)
|
|
131
|
+
if override is not None:
|
|
132
|
+
return override(mob, **kwargs) if kwargs else override(mob)
|
|
133
|
+
defaults = _REGISTRY.get()
|
|
134
|
+
for cls in type(mob).__mro__:
|
|
135
|
+
factory = defaults.get(cls)
|
|
136
|
+
if factory is not None:
|
|
137
|
+
return factory(mob, **kwargs) if kwargs else factory(mob)
|
|
138
|
+
from manim import FadeOut
|
|
139
|
+
|
|
140
|
+
return FadeOut(mob, **kwargs)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def Remove(mob: Any, **kwargs: Any) -> Any: # noqa: N802 -- mirrors Manim's PascalCase Animations
|
|
144
|
+
"""Alias for ``exit_for(mob, **kwargs)`` -- spelled to match Manim animations."""
|
|
145
|
+
return exit_for(mob, **kwargs)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def clear_scene(scene: Any, *, exclude: Iterable[Any] = ()) -> None:
|
|
149
|
+
"""Play exit animations for every Mobject not in ``exclude``.
|
|
150
|
+
|
|
151
|
+
Uses ``scene.mobjects_without_canvas`` when available (so the
|
|
152
|
+
manim-slides canvas survives) and dispatches through ``exit_for`` so
|
|
153
|
+
per-instance and per-type overrides apply.
|
|
154
|
+
"""
|
|
155
|
+
skip = set(exclude)
|
|
156
|
+
pool = getattr(scene, "mobjects_without_canvas", None) or scene.mobjects
|
|
157
|
+
targets = [m for m in pool if m not in skip]
|
|
158
|
+
if not targets:
|
|
159
|
+
return
|
|
160
|
+
scene.play(*(exit_for(m) for m in targets))
|