mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Compare commits
486 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e83f4b09a |
||
|
|
f7fd708276 |
||
|
|
4c4622df54 |
||
|
|
66d5a4937a |
||
|
|
c07137dbed |
||
|
|
71ab85f960 |
||
|
|
16f0a3de3e |
||
|
|
537a134360 |
||
|
|
b7bf2ea90f |
||
|
|
bdaf4497b7 |
||
|
|
037b376ec2 |
||
|
|
e9639c2697 |
||
|
|
1b2d5ce72b |
||
|
|
c94a7ea9fc |
||
|
|
bb1be6ef8a |
||
|
|
516c8c8ba7 |
||
|
|
ccee37a614 |
||
|
|
31db147222 |
||
|
|
34124c3f60 |
||
|
|
d999d422c9 |
||
|
|
561de9d72a |
||
|
|
05b3042ab0 |
||
|
|
2ece488b2c |
||
|
|
852ebd1c60 |
||
|
|
82522795f1 |
||
|
|
12c5640a32 |
||
|
|
33424fe43d |
||
|
|
1b3390073c |
||
|
|
56f7eb2a1f |
||
|
|
cfb5c684b7 |
||
|
|
429f25328d |
||
|
|
c45724989d |
||
|
|
af70b6fef2 |
||
|
|
4b32312dd1 |
||
|
|
90141df105 |
||
|
|
82f93b6c3c |
||
|
|
752b46a003 |
||
|
|
21cf9998cc |
||
|
|
ebb230f6f1 |
||
|
|
46177d247e |
||
|
|
468929889b |
||
|
|
98c458b6b2 |
||
|
|
d4af5b2baa |
||
|
|
1157b746c3 |
||
|
|
6f825e8513 |
||
|
|
a0414dccec |
||
|
|
33a0e56d73 |
||
|
|
80fd11efbc |
||
|
|
498f0b9c89 |
||
|
|
87cd63549c |
||
|
|
cd370610c5 |
||
|
|
a6af7f3d76 |
||
|
|
e34e707858 |
||
|
|
7c1c9258d0 |
||
|
|
000e7792bd |
||
|
|
704830ff5f | ||
|
|
d1eea48aa6 |
||
|
|
9504757918 |
||
|
|
7eab4c5450 |
||
|
|
fda336978b |
||
|
|
ab7cfc54d8 |
||
|
|
601a007192 |
||
|
|
ab17eb58a3 |
||
|
|
a5d4ab134c |
||
|
|
f9b12e5d12 |
||
|
|
ae501baf19 |
||
|
|
761bc46cc8 |
||
|
|
357bb3fbba |
||
|
|
6e9ec60b6f |
||
|
|
c8db165825 |
||
|
|
b4049bd6c7 |
||
|
|
8a5267a9ee |
||
|
|
6adc6e4cf6 |
||
|
|
21e7e0d9c1 |
||
|
|
7efa45492f |
||
|
|
c261c61dfd |
||
|
|
9d98f00ee9 |
||
|
|
587e8d6546 |
||
|
|
d49115316e |
||
|
|
2f38426ceb |
||
|
|
bbdcda1ff5 |
||
|
|
2c0b49be9f |
||
|
|
eb8194a640 |
||
|
|
eca7f17853 |
||
|
|
2317b0ee8b |
||
|
|
d938533742 |
||
|
|
0b3f28a5af |
||
|
|
6a56cc5ce6 |
||
|
|
cd7cd1b219 |
||
|
|
9a6550d356 |
||
|
|
06f2fe231a |
||
|
|
d64af99f89 |
||
|
|
2e0aa8f529 |
||
|
|
88718ce43f |
||
|
|
73eeacb880 |
||
|
|
739c2c3f14 |
||
|
|
d75f84a985 |
||
|
|
b59f311331 |
||
|
|
dc4a8bb741 |
||
|
|
4bc77b3a00 |
||
|
|
c424f83cb4 |
||
|
|
0d2533231b |
||
|
|
b24b61776b |
||
|
|
d308ae3372 |
||
|
|
ff86ee6333 | ||
|
|
ef0cf2a34d | ||
|
|
75285d1f01 | ||
|
|
d60154d024 |
||
|
|
976d634656 |
||
|
|
765f02f3ed |
||
|
|
a50d8363e7 |
||
|
|
de090c1bd0 |
||
|
|
e85bfca015 |
||
|
|
0541f9e6f1 | ||
|
|
c78de89e64 | ||
|
|
ab045f0ae1 | ||
|
|
52820c6b65 |
||
|
|
51aa01acea |
||
|
|
fcb9f6fee3 |
||
|
|
adb0915605 |
||
|
|
b6a464ed64 |
||
|
|
3bf384ab1e |
||
|
|
a1bca6c3b9 |
||
|
|
51377b530c |
||
|
|
3c6ea0e2d6 |
||
|
|
00d0b297b4 |
||
|
|
83bcafb0e7 |
||
|
|
7e950b38fa |
||
|
|
9528337ad4 |
||
|
|
afa4692f7b |
||
|
|
038d16b531 |
||
|
|
3e8f41c9be |
||
|
|
4dd2937f1e |
||
|
|
a810f5535c |
||
|
|
dd04fce52a |
||
|
|
e7d69834b3 |
||
|
|
7ffdf04dcb |
||
|
|
e489ebf220 |
||
|
|
ce2c3c43e4 |
||
|
|
5ccc5bac19 |
||
|
|
e6777d6334 |
||
|
|
855ea863b6 |
||
|
|
8535d0675b |
||
|
|
3ea23e9cd0 |
||
|
|
7e2ea86e98 |
||
|
|
6b668784f2 |
||
|
|
dd91a8a2ee |
||
|
|
ec306075a9 |
||
|
|
2d3aa0d2f4 |
||
|
|
a56c06cd51 |
||
|
|
03a94141d2 |
||
|
|
5a2b3384f1 |
||
|
|
c4dc0ea86d |
||
|
|
e822de0315 |
||
|
|
f69f2545d9 |
||
|
|
48d5a36aad |
||
|
|
05cc414014 |
||
|
|
b3df1cf79e |
||
|
|
cb8af6f2d0 |
||
|
|
d8a7e55ee1 |
||
|
|
2d8d81c209 |
||
|
|
fc68c10433 |
||
|
|
d0521fa06d |
||
|
|
e339a68e54 |
||
|
|
c887b51d63 |
||
|
|
ded54e41ae |
||
|
|
04503adb70 |
||
|
|
9c9ebf428e |
||
|
|
d18dc8f89b |
||
|
|
7eb88562c2 |
||
|
|
aa0cd4fe82 |
||
|
|
a9b65eed5f |
||
|
|
dba6fa8f99 |
||
|
|
df36f4f196 |
||
|
|
7ea765a759 |
||
|
|
21fe73b5c2 |
||
|
|
088affd876 |
||
|
|
c7ff795c0a |
||
|
|
dea245ad9a |
||
|
|
a8c16fbfb1 |
||
|
|
83d4301184 |
||
|
|
b65b0f2470 |
||
|
|
4ddf77739d |
||
|
|
e73b6590dd |
||
|
|
fd8ab62613 |
||
|
|
047db453ff |
||
|
|
2f02eb9328 |
||
|
|
c1663f1a1e |
||
|
|
2790a70bc5 |
||
|
|
fdb5cb9f04 |
||
|
|
ec501b960c |
||
|
|
e54113e263 |
||
|
|
b83410aee0 |
||
|
|
f124235a9b |
||
|
|
9e74ee7edf |
||
|
|
3721fb1612 |
||
|
|
3d029c1280 |
||
|
|
f63ba71f04 |
||
|
|
a234ff31e8 |
||
|
|
9c43ad0d21 |
||
|
|
ea16d22735 |
||
|
|
c4b7a80258 |
||
|
|
0cec80216b |
||
|
|
a1bb04947a |
||
|
|
325fa08a72 |
||
|
|
a8458cb329 |
||
|
|
64862994ac |
||
|
|
c9707f281d |
||
|
|
23a2df1e5a |
||
|
|
3377f6c1f6 |
||
|
|
e4b1d105a3 |
||
|
|
204e44a106 |
||
|
|
d517b50567 |
||
|
|
5301ab874f |
||
|
|
be6a9dfaa7 |
||
|
|
e569fd47ad |
||
|
|
bf72127152 |
||
|
|
ffa6ffa548 |
||
|
|
bcab73a3d2 |
||
|
|
fdc496c211 |
||
|
|
f6cdb547d2 |
||
|
|
5a3dcf77fa |
||
|
|
c53ac5544a |
||
|
|
7f6743e97c |
||
|
|
a2876dc77c |
||
|
|
de210bad8b |
||
|
|
ab4855728f |
||
|
|
f304bd93ea |
||
|
|
384350cf3b |
||
|
|
593010b87b |
||
|
|
3c6a1ee80b |
||
|
|
44da20d71d |
||
|
|
e1925e46dc |
||
|
|
2ab99b53d3 |
||
|
|
2bdd5ac230 |
||
|
|
9479febef2 |
||
|
|
d85ddb0ca5 |
||
|
|
cd2a3b92f6 |
||
|
|
01fc1ef71f |
||
|
|
d521bc9104 |
||
|
|
95fba7da10 |
||
|
|
a6990bd83c |
||
|
|
8f919b1726 |
||
|
|
6ca08fd187 |
||
|
|
106b849a6d |
||
|
|
c2dfb597ff |
||
|
|
765c090027 |
||
|
|
8bb28aae8e |
||
|
|
bad88a11f9 |
||
|
|
1efe869293 |
||
|
|
86a971940f |
||
|
|
97f818eb7f |
||
|
|
c933c74a40 |
||
|
|
530dd4d782 |
||
|
|
95b4625ef2 |
||
|
|
def7241db3 |
||
|
|
f18a2b46e5 |
||
|
|
9d51c0d44d |
||
|
|
9e45cf6ff8 |
||
|
|
7879fe4377 |
||
|
|
17d4a7f635 |
||
|
|
dbad8a86bc |
||
|
|
112c99ba4a |
||
|
|
46dbf3d3ed |
||
|
|
6abcc611b9 |
||
|
|
953888e68e |
||
|
|
d485986122 |
||
|
|
263623e10c |
||
|
|
73d22454f1 |
||
|
|
e74933049e |
||
|
|
f81d5713b3 |
||
|
|
862504fa45 |
||
|
|
fac0aa5218 |
||
|
|
faecdd3365 |
||
|
|
8f6eaf372e |
||
|
|
c516932f9f |
||
|
|
ab577dc2ea |
||
|
|
a21a5e5719 |
||
|
|
97efef4542 |
||
|
|
ee0501c2b6 |
||
|
|
c6edb330a9 |
||
|
|
9f1f239f10 |
||
|
|
2570a251c9 |
||
|
|
f65b7f8085 |
||
|
|
20f44b456b |
||
|
|
87732527d3 |
||
|
|
a395ffd698 |
||
|
|
327db11c54 |
||
|
|
d5b8b41283 |
||
|
|
39382e6ba8 |
||
|
|
96e58aeb05 |
||
|
|
ce1fff645d |
||
|
|
fc58a46615 |
||
|
|
225efcad22 |
||
|
|
0cdfd4b304 |
||
|
|
75a7525ed5 |
||
|
|
5788f8131f |
||
|
|
0a96aacb24 |
||
|
|
d6f066c131 |
||
|
|
1dcb81268a |
||
|
|
74f79a42b6 |
||
|
|
2536b7fafb |
||
|
|
1097ed9692 |
||
|
|
8deac51abc |
||
|
|
76e6a57164 |
||
|
|
95966a7f61 |
||
|
|
32cc7abee1 |
||
|
|
2462d0d011 |
||
|
|
d029beb2b7 |
||
|
|
99ce78fd60 |
||
|
|
aa660c77ec |
||
|
|
c0b04876db |
||
|
|
0ac03869f5 |
||
|
|
11ea41633c |
||
|
|
17e5a77b09 |
||
|
|
85d9ac8f47 |
||
|
|
5bda1075e6 |
||
|
|
c47b2d5f44 |
||
|
|
2075f82bed |
||
|
|
20d0194074 |
||
|
|
3a4ab4cd25 |
||
|
|
d6dac76fa3 |
||
|
|
3aad3553d8 |
||
|
|
f463423abe |
||
|
|
0843632932 |
||
|
|
c8fe4d9e4f |
||
|
|
fa65b488b0 |
||
|
|
a03e73c822 |
||
|
|
f1ce512225 |
||
|
|
205f746bbd |
||
|
|
b064af9abb |
||
|
|
374eeeba0d |
||
|
|
3a71411527 |
||
|
|
1df1709e49 |
||
|
|
628a54535b |
||
|
|
75625964b6 |
||
|
|
67f95db213 |
||
|
|
bf2feee19e |
||
|
|
246e44b3d0 |
||
|
|
bb98eca37a |
||
|
|
8614b9832e |
||
|
|
73200d6644 |
||
|
|
1260346f4e |
||
|
|
d117c9f4b5 |
||
|
|
8d70b0e886 |
||
|
|
64ab9ade2b |
||
|
|
0d21a7e6ed |
||
|
|
93a20cd9e7 |
||
|
|
a70aeee723 |
||
|
|
d5cdd79709 |
||
|
|
fdb4e37f75 |
||
|
|
89d5ed4398 |
||
|
|
82ff488f9c |
||
|
|
56354af1e5 |
||
|
|
7802e73b88 |
||
|
|
3ed86a820b |
||
|
|
4641c0a808 |
||
|
|
ece1235d49 |
||
|
|
c2fa4f926b |
||
|
|
e30ed6ed82 |
||
|
|
e74f37ed68 |
||
|
|
252aac30da |
||
|
|
aec3d86122 |
||
|
|
31df6d0a23 |
||
|
|
90ae6ad804 |
||
|
|
938b8fc344 |
||
|
|
0a2fbbeffb |
||
|
|
c9776be7da |
||
|
|
24025b60d5 |
||
|
|
21eb0b7769 |
||
|
|
0d67dcdc6e |
||
|
|
739958514f |
||
|
|
7b841c27cc |
||
|
|
b4156045a3 |
||
|
|
203a5366c2 |
||
|
|
1f249e45b9 |
||
|
|
4bd2f860b4 |
||
|
|
94cd27c234 |
||
|
|
e4ee9a06a1 |
||
|
|
93c601f013 |
||
|
|
31f189519b |
||
|
|
a56c0c9fee |
||
|
|
4a0c7057db |
||
|
|
33e560473d |
||
|
|
6cb89a06ba |
||
|
|
c4e7502968 |
||
|
|
1ce3edd97a |
||
|
|
98641a2f82 |
||
|
|
a3d584b7f4 |
||
|
|
791a892be3 |
||
|
|
0f268e6f9c |
||
|
|
a3adcaaab3 |
||
|
|
aa3e2cf54e |
||
|
|
c32f0fd83a |
||
|
|
a4e5233273 |
||
|
|
1aee37bfb5 |
||
|
|
0fd16b8918 |
||
|
|
563810a772 |
||
|
|
dff83be61f |
||
|
|
83957b9c2f |
||
|
|
f8e3408f9e |
||
|
|
679b89a43f |
||
|
|
f9dc9c7a5b |
||
|
|
5139765ff4 |
||
|
|
04bfa22107 |
||
|
|
dc0db41245 |
||
|
|
a45c0d4b67 |
||
|
|
27766127e2 |
||
|
|
2a4c2da1d7 |
||
|
|
902c3f4215 |
||
|
|
aef8184379 |
||
|
|
b750152700 |
||
|
|
93cf85cd42 |
||
|
|
294313d683 |
||
|
|
a6da37b88c |
||
|
|
d62a04f059 |
||
|
|
7936c7d08a |
||
|
|
1aad0c7b1a |
||
|
|
c025be6516 |
||
|
|
ef675b64f0 |
||
|
|
afe1d18de5 |
||
|
|
909ffded28 |
||
|
|
7a794e341f |
||
|
|
d1cf1c5750 |
||
|
|
f65ebf2194 |
||
|
|
9f3d486095 |
||
|
|
77d42d2a46 |
||
|
|
c7e7ca2e46 |
||
|
|
206f8740b9 |
||
|
|
fcd81b21a4 |
||
|
|
011c36a634 |
||
|
|
ed1b203993 |
||
|
|
1520481812 |
||
|
|
65d352d0ab |
||
|
|
f70980aa40 |
||
|
|
e2cec98f9a |
||
|
|
d0fe0c3894 |
||
|
|
f8b5066899 |
||
|
|
175cb89400 |
||
|
|
7006d6b79e |
||
|
|
8264595e3b |
||
|
|
9289a5c68d |
||
|
|
9d880c2f80 |
||
|
|
775482bc20 |
||
|
|
68bd79093e |
||
|
|
a57fb69f68 |
||
|
|
9a84ec6a45 |
||
|
|
4e3cfd207a |
||
|
|
3b496ea2e6 |
||
|
|
286f366a35 |
||
|
|
02391d71ac |
||
|
|
4a963c45ab |
||
|
|
472eaae6fe |
||
|
|
9b18a861f3 |
||
|
|
34e7d68644 |
||
|
|
3037f0e713 |
||
|
|
dd327a37ca |
||
|
|
8ea8f25576 |
||
|
|
196fa1fa2f |
||
|
|
cac608bf2a | ||
|
|
b69e1d72cf |
||
|
|
fc4271061c |
||
|
|
98a7f42ed2 |
||
|
|
17043747ef |
||
|
|
d39097825a |
||
|
|
9938bf59a3 |
||
|
|
0a24cad22a |
||
|
|
912ae76f8f |
||
|
|
18a7294164 |
||
|
|
9a35756048 |
||
|
|
64a0e9db43 |
||
|
|
018e4a3c1a |
||
|
|
03f9d4b90b |
||
|
|
3f118e4921 |
||
|
|
370d883374 |
||
|
|
0cba710a38 |
||
|
|
27d53606d4 |
||
|
|
40766cb648 |
||
|
|
7590086753 |
||
|
|
61a2d05b69 |
||
|
|
6949c66922 |
||
|
|
7cead84d21 |
||
|
|
28bf4dd2a6 |
||
|
|
212bca09ca |
||
|
|
94711f742d |
||
|
|
4315549c31 |
554 changed files with 31776 additions and 16496 deletions
|
|
@ -1 +0,0 @@
|
|||
<path id="nd" d="m 464.7,68.6 -1.1,2.8 .8,1.4 -.3,5.1 -.5,1.1 2.7,9.1 1.3,2.5 .7,14 1,2.7 -.4,5.8 2.9,7.4 .3,5.8 -.1,2.1 -29.5,-.4 -46,-2.1 -39.2,-2.9 5.2,-66.7 44.5,3.4 55.3,1.6 z">
|
||||
5
.codespell_ignorewords
Normal file
5
.codespell_ignorewords
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
nam
|
||||
sherif
|
||||
falsy
|
||||
medias
|
||||
strager
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[codespell]
|
||||
exclude-file=.codespell_ignorelines
|
||||
check-hidden=True
|
||||
ignore-words-list = nam,sherif,falsy
|
||||
check-hidden = True
|
||||
skip = .git,*.js,*.js.map,*.css,*.css.map,*.html,*.po,*.pot,uv.lock,*.log,*.svg
|
||||
ignore-words = .codespell_ignorewords
|
||||
|
|
|
|||
|
|
@ -1 +1,20 @@
|
|||
.git
|
||||
|
||||
# Development / test artifacts
|
||||
__pycache__
|
||||
**/__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.egg-info
|
||||
dist/
|
||||
build/
|
||||
coverage.xml
|
||||
|
||||
# Not needed to install the package
|
||||
docs/
|
||||
tests/
|
||||
example_scenes/
|
||||
media/
|
||||
logo/
|
||||
scripts/
|
||||
|
|
|
|||
8
.flake8
8
.flake8
|
|
@ -2,7 +2,7 @@
|
|||
# Exclude the grpc generated code
|
||||
exclude = ./manim/grpc/gen/*, __pycache__,.git,
|
||||
per-file-ignores = __init__.py:F401
|
||||
max-complexity = 15
|
||||
max-complexity = 29
|
||||
max-line-length = 88
|
||||
statistics = True
|
||||
# Prevents some flake8-rst-docstrings errors
|
||||
|
|
@ -18,6 +18,9 @@ extend-ignore = E203, W503, D202, D212, D213, D404
|
|||
# Misc
|
||||
F401, F403, F405, F841, E501, E731, E402, F811, F821,
|
||||
|
||||
# multiple statements on one line (overload)
|
||||
E704,
|
||||
|
||||
# Plug-in: flake8-builtins
|
||||
A001, A002, A003,
|
||||
|
||||
|
|
@ -27,9 +30,6 @@ extend-ignore = E203, W503, D202, D212, D213, D404
|
|||
# Plug-in: flake8-simplify
|
||||
SIM105, SIM106, SIM119,
|
||||
|
||||
# Plug-in: flake8-comprehensions
|
||||
C901
|
||||
|
||||
# Plug-in: flake8-pytest-style
|
||||
PT001, PT004, PT006, PT011, PT018, PT022, PT023,
|
||||
|
||||
|
|
|
|||
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Switched to ruff format:
|
||||
24025b60d57301b0a59754c38d77bccd8ed69feb
|
||||
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -70,14 +70,5 @@ PASTE HERE
|
|||
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
|
||||
</details>
|
||||
|
||||
<details><summary>FFMPEG</summary>
|
||||
|
||||
Output of `ffmpeg -version`:
|
||||
|
||||
```
|
||||
PASTE HERE
|
||||
```
|
||||
</details>
|
||||
|
||||
## Additional comments
|
||||
<!-- Add further context that you think might be relevant for this issue here. -->
|
||||
|
|
|
|||
9
.github/ISSUE_TEMPLATE/installation_issue.md
vendored
9
.github/ISSUE_TEMPLATE/installation_issue.md
vendored
|
|
@ -53,14 +53,5 @@ PASTE HERE
|
|||
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
|
||||
</details>
|
||||
|
||||
<details><summary>FFMPEG</summary>
|
||||
|
||||
Output of `ffmpeg -version`:
|
||||
|
||||
```
|
||||
PASTE HERE
|
||||
```
|
||||
</details>
|
||||
|
||||
## Additional comments
|
||||
<!-- Add further context that you think might be relevant for this issue here. -->
|
||||
|
|
|
|||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,10 +1,6 @@
|
|||
<!-- Thank you for contributing to Manim! Learn more about the process in our contributing guidelines: https://docs.manim.community/en/latest/contributing.html -->
|
||||
|
||||
## Overview: What does this pull request change?
|
||||
<!-- If there is more information than the PR title that should be added to our release changelog, add it in the following changelog section. This is optional, but recommended for larger pull requests. -->
|
||||
<!--changelog-start-->
|
||||
|
||||
<!--changelog-end-->
|
||||
|
||||
## Motivation and Explanation: Why and how do your changes improve the library?
|
||||
<!-- Optional for bugfixes, small enhancements, and documentation-related PRs. Otherwise, please give a short reasoning for your changes. -->
|
||||
|
|
|
|||
6
.github/codeql.yml
vendored
6
.github/codeql.yml
vendored
|
|
@ -9,6 +9,12 @@ query-filters:
|
|||
id: py/multiple-calls-to-init
|
||||
- exclude:
|
||||
id: py/missing-call-to-init
|
||||
- exclude:
|
||||
id: py/method-first-arg-is-not-self
|
||||
- exclude:
|
||||
id: py/cyclic-import
|
||||
- exclude:
|
||||
id: py/unsafe-cyclic-import
|
||||
paths:
|
||||
- manim
|
||||
paths-ignore:
|
||||
|
|
|
|||
10
.github/manimdependency.json
vendored
10
.github/manimdependency.json
vendored
|
|
@ -4,7 +4,10 @@
|
|||
"standalone",
|
||||
"preview",
|
||||
"doublestroke",
|
||||
"ms",
|
||||
"count1to",
|
||||
"multitoc",
|
||||
"prelim2e",
|
||||
"ragged2e",
|
||||
"everysel",
|
||||
"setspace",
|
||||
"rsfs",
|
||||
|
|
@ -29,7 +32,10 @@
|
|||
"standalone",
|
||||
"preview",
|
||||
"doublestroke",
|
||||
"ms",
|
||||
"count1to",
|
||||
"multitoc",
|
||||
"prelim2e",
|
||||
"ragged2e",
|
||||
"everysel",
|
||||
"setspace",
|
||||
"rsfs",
|
||||
|
|
|
|||
67
.github/release.yml
vendored
Normal file
67
.github/release.yml
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
|
||||
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- duplicate/wontfix
|
||||
- invalid
|
||||
- question
|
||||
- release
|
||||
authors:
|
||||
- dependabot[bot]
|
||||
- pre-commit-ci[bot]
|
||||
|
||||
categories:
|
||||
# High Impact
|
||||
- title: "Breaking Changes 🚨"
|
||||
labels:
|
||||
- breaking changes
|
||||
|
||||
# Highlights
|
||||
- title: "Highlights 🌟"
|
||||
labels:
|
||||
- highlight
|
||||
|
||||
# User-facing
|
||||
- title: "New Features ✨"
|
||||
labels:
|
||||
- new feature
|
||||
|
||||
- title: "Enhancements 🚀"
|
||||
labels:
|
||||
- enhancement
|
||||
|
||||
- title: "Bug Fixes 🐛"
|
||||
labels:
|
||||
- pr:bugfix
|
||||
|
||||
- title: "Deprecations & Removals ⚠️"
|
||||
labels:
|
||||
- pr:deprecation
|
||||
|
||||
# Developer-facing
|
||||
- title: "Documentation 📚"
|
||||
labels:
|
||||
- documentation
|
||||
|
||||
- title: "Testing 🧪"
|
||||
labels:
|
||||
- testing
|
||||
|
||||
- title: "Infrastructure & Build 🔨"
|
||||
labels:
|
||||
- infrastructure
|
||||
|
||||
- title: "Code Quality & Refactoring 🧹"
|
||||
labels:
|
||||
- maintenance
|
||||
- refactor
|
||||
|
||||
- title: "Type Hints 📝"
|
||||
labels:
|
||||
- typehints
|
||||
|
||||
# Catch-all (must be last)
|
||||
- title: "Other Changes"
|
||||
labels:
|
||||
- "*"
|
||||
40
.github/scripts/ci_build_cairo.py
vendored
40
.github/scripts/ci_build_cairo.py
vendored
|
|
@ -14,8 +14,8 @@ import subprocess
|
|||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import typing
|
||||
import urllib.request
|
||||
from collections.abc import Generator
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from sys import stdout
|
||||
|
|
@ -67,7 +67,7 @@ def run_command(command, cwd=None, env=None):
|
|||
|
||||
|
||||
@contextmanager
|
||||
def gha_group(title: str) -> typing.Generator:
|
||||
def gha_group(title: str) -> Generator:
|
||||
if not is_ci():
|
||||
yield
|
||||
return
|
||||
|
|
@ -144,10 +144,38 @@ def main():
|
|||
]
|
||||
)
|
||||
|
||||
env_vars = {
|
||||
# add the venv bin directory to PATH so that meson can find ninja
|
||||
"PATH": f"{os.path.join(tmpdir, VENV_NAME, 'bin')}{os.pathsep}{os.environ['PATH']}",
|
||||
}
|
||||
# Inherit the current environment so PKG_CONFIG_PATH, CFLAGS, LDFLAGS, etc. are preserved.
|
||||
env_vars = os.environ.copy()
|
||||
# Prepend the venv bin directory so meson/ninja from the venv are used.
|
||||
env_vars["PATH"] = f"{os.path.join(tmpdir, VENV_NAME, 'bin')}{os.pathsep}{env_vars.get('PATH','')}"
|
||||
|
||||
# Ensure Homebrew-provided pkgconfig and include/lib paths are present on macOS ARM.
|
||||
if sys.platform == "darwin":
|
||||
try:
|
||||
# Try to get specific prefix for lzo (safer for opt path), fall back to generic brew prefix.
|
||||
brew_prefix = subprocess.check_output(["brew", "--prefix", "lzo"], text=True).strip()
|
||||
except subprocess.CalledProcessError:
|
||||
try:
|
||||
brew_prefix = subprocess.check_output(["brew", "--prefix"], text=True).strip()
|
||||
except Exception:
|
||||
brew_prefix = None
|
||||
|
||||
if brew_prefix:
|
||||
# pkg-config files can live in lib/pkgconfig or opt/<pkg>/lib/pkgconfig
|
||||
pkgconfig_paths = [f"{brew_prefix}/lib/pkgconfig", f"{brew_prefix}/opt/lzo/lib/pkgconfig"]
|
||||
# merge with any existing PKG_CONFIG_PATH
|
||||
existing_pc = env_vars.get("PKG_CONFIG_PATH", "")
|
||||
merged_pc = ":".join([p for p in pkgconfig_paths if p]) + (f":{existing_pc}" if existing_pc else "")
|
||||
env_vars["PKG_CONFIG_PATH"] = merged_pc
|
||||
|
||||
# Ensure compiler & linker flags include brew include/lib
|
||||
existing_cflags = env_vars.get("CFLAGS", "")
|
||||
existing_ldflags = env_vars.get("LDFLAGS", "")
|
||||
env_vars["CFLAGS"] = f"-I{brew_prefix}/include {existing_cflags}".strip()
|
||||
env_vars["LDFLAGS"] = f"-L{brew_prefix}/lib {existing_ldflags}".strip()
|
||||
|
||||
# Debugging: log environment keys relevant to detection
|
||||
# logger.info(f"env vars for meson: {env_vars}")
|
||||
|
||||
with gha_group("Building and Installing Cairo"):
|
||||
logger.info("Running meson setup")
|
||||
|
|
|
|||
2
.github/workflows/cffconvert.yml
vendored
2
.github/workflows/cffconvert.yml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out a copy of the repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Check whether the citation metadata from CITATION.cff is valid
|
||||
uses: citation-file-format/cffconvert-github-action@2.0.0
|
||||
|
|
|
|||
77
.github/workflows/ci.yml
vendored
77
.github/workflows/ci.yml
vendored
|
|
@ -18,31 +18,29 @@ jobs:
|
|||
env:
|
||||
DISPLAY: :0
|
||||
PYTEST_ADDOPTS: "--color=yes" # colors in pytest
|
||||
PYTHONIOENCODING: "utf8"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
python: ["3.9", "3.10", "3.11", "3.12"]
|
||||
python: ["3.11", "3.12", "3.13", "3.14"]
|
||||
include:
|
||||
- os: macos-15-intel
|
||||
python: "3.13"
|
||||
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
pipx install "poetry==1.5.*"
|
||||
poetry config virtualenvs.prefer-active-python true
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
cache: "poetry"
|
||||
|
||||
- name: Setup macOS PATH
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: true
|
||||
|
||||
- name: Setup cache variables
|
||||
shell: bash
|
||||
|
|
@ -50,25 +48,22 @@ jobs:
|
|||
run: |
|
||||
echo "date=$(/bin/date -u "+%m%w%Y")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install and cache ffmpeg (all OS)
|
||||
uses: FedericoCarboni/setup-ffmpeg@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: setup-ffmpeg
|
||||
|
||||
- name: Install system dependencies (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: python3-opengl libpango1.0-dev xvfb
|
||||
packages: python3-opengl libpango1.0-dev xvfb freeglut3-dev
|
||||
version: 1.0
|
||||
|
||||
- name: Install Texlive (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
uses: teatimeguest/setup-texlive-action@v3
|
||||
uses: zauguin/install-texlive@v4
|
||||
with:
|
||||
cache: true
|
||||
packages: scheme-basic fontspec inputenc fontenc tipa mathrsfs calligra xcolor standalone preview doublestroke ms everysel setspace rsfs relsize ragged2e fundus-calligra microtype wasysym physics dvisvgm jknapltx wasy cm-super babel-english gnu-freefont mathastext cbfonts-fd xetex
|
||||
packages: >
|
||||
scheme-basic latex fontspec tipa calligra xcolor
|
||||
standalone preview doublestroke setspace rsfs relsize
|
||||
ragged2e fundus-calligra microtype wasysym physics dvisvgm jknapltx
|
||||
wasy cm-super babel-english gnu-freefont mathastext cbfonts-fd xetex
|
||||
|
||||
- name: Start virtual display (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
|
|
@ -77,12 +72,12 @@ jobs:
|
|||
sudo /usr/bin/Xvfb $DISPLAY -screen 0 1280x1024x24 &
|
||||
|
||||
- name: Setup Cairo Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v5
|
||||
id: cache-cairo
|
||||
if: runner.os == 'Linux' || runner.os == 'macOS'
|
||||
with:
|
||||
path: ${{ github.workspace }}/third_party
|
||||
key: ${{ runner.os }}-dependencies-cairo-${{ hashFiles('.github/scripts/ci_build_cairo.py') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-dependencies-cairo-${{ hashFiles('.github/scripts/ci_build_cairo.py') }}
|
||||
|
||||
- name: Build and install Cairo (Linux and macOS)
|
||||
if: (runner.os == 'Linux' || runner.os == 'macOS') && steps.cache-cairo.outputs.cache-hit != 'true'
|
||||
|
|
@ -93,7 +88,7 @@ jobs:
|
|||
run: python .github/scripts/ci_build_cairo.py --set-env-vars
|
||||
|
||||
- name: Setup macOS cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v5
|
||||
id: cache-macos
|
||||
if: runner.os == 'macOS'
|
||||
with:
|
||||
|
|
@ -109,8 +104,8 @@ jobs:
|
|||
oriPath=$PATH
|
||||
sudo mkdir -p $PWD/macos-cache
|
||||
echo "Install TinyTeX"
|
||||
sudo curl -L -o "/tmp/TinyTeX.tgz" "https://github.com/yihui/tinytex-releases/releases/download/daily/TinyTeX-1.tgz"
|
||||
sudo tar zxf "/tmp/TinyTeX.tgz" -C "$PWD/macos-cache"
|
||||
sudo curl -L -o "/tmp/TinyTeX.tar.xz" "https://github.com/rstudio/tinytex-releases/releases/download/daily/TinyTeX-1-darwin.tar.xz"
|
||||
sudo tar xJf "/tmp/TinyTeX.tar.xz" -C "$PWD/macos-cache"
|
||||
export PATH="$PWD/macos-cache/TinyTeX/bin/universal-darwin:$PATH"
|
||||
sudo tlmgr update --self
|
||||
for i in "${ttp[@]}"; do
|
||||
|
|
@ -124,18 +119,17 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
echo "/Library/TeX/texbin" >> $GITHUB_PATH
|
||||
echo "$HOME/.poetry/bin" >> $GITHUB_PATH
|
||||
echo "$PWD/macos-cache/TinyTeX/bin/universal-darwin" >> $GITHUB_PATH
|
||||
|
||||
- name: Setup Windows cache
|
||||
id: cache-windows
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}\ManimCache
|
||||
key: ${{ runner.os }}-dependencies-tinytex-${{ hashFiles('.github/manimdependency.json') }}-${{ steps.cache-vars.outputs.date }}-1
|
||||
|
||||
- uses: ssciwr/setup-mesa-dist-win@v1
|
||||
- uses: ssciwr/setup-mesa-dist-win@v3
|
||||
|
||||
- name: Install system dependencies (Windows)
|
||||
if: runner.os == 'Windows' && steps.cache-windows.outputs.cache-hit != 'true'
|
||||
|
|
@ -143,14 +137,11 @@ jobs:
|
|||
$tinyTexPackages = $(python -c "import json;print(' '.join(json.load(open('.github/manimdependency.json'))['windows']['tinytex']))") -Split ' '
|
||||
$OriPath = $env:PATH
|
||||
echo "Install Tinytex"
|
||||
Invoke-WebRequest "https://github.com/yihui/tinytex-releases/releases/download/daily/TinyTeX-1.zip" -O "$($env:TMP)\TinyTex.zip"
|
||||
Expand-Archive -LiteralPath "$($env:TMP)\TinyTex.zip" -DestinationPath "$($PWD)\ManimCache\LatexWindows"
|
||||
Invoke-WebRequest "https://github.com/rstudio/tinytex-releases/releases/download/daily/TinyTeX-1-windows.exe" -OutFile "$($env:TMP)\TinyTex.exe"
|
||||
.$env:TMP\TinyTex.exe -o"$($PWD)\ManimCache\LatexWindows"
|
||||
$env:Path = "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\windows;$($env:PATH)"
|
||||
tlmgr update --self
|
||||
foreach ($c in $tinyTexPackages){
|
||||
$c=$c.Trim()
|
||||
tlmgr install $c
|
||||
}
|
||||
tlmgr install $tinyTexPackages
|
||||
$env:PATH=$OriPath
|
||||
echo "Completed Latex"
|
||||
|
||||
|
|
@ -158,22 +149,20 @@ jobs:
|
|||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
$env:Path += ";" + "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\windows"
|
||||
$env:Path = "$env:USERPROFILE\.poetry\bin;$($env:PATH)"
|
||||
echo "$env:Path" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
|
||||
- name: Install manim
|
||||
- name: Install dependencies and manim
|
||||
run: |
|
||||
poetry config installer.modern-installation false
|
||||
poetry install
|
||||
uv sync --all-extras --locked
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
poetry run python -m pytest
|
||||
uv run python -m pytest
|
||||
|
||||
- name: Run module doctests
|
||||
run: |
|
||||
poetry run python -m pytest -v --cov-append --ignore-glob="*opengl*" --doctest-modules manim
|
||||
uv run python -m pytest -v --cov-append --ignore-glob="*opengl*" --doctest-modules manim
|
||||
|
||||
- name: Run doctests in rst files
|
||||
run: |
|
||||
cd docs && poetry run make doctest O=-tskip-manim
|
||||
cd docs && uv run make doctest O=-tskip-manim
|
||||
|
|
|
|||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
|
|
@ -24,19 +24,19 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config-file: ./.github/codeql.yml
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
|
|
|||
16
.github/workflows/publish-docker.yml
vendored
16
.github/workflows/publish-docker.yml
vendored
|
|
@ -13,19 +13,19 @@ jobs:
|
|||
if: github.event_name != 'release'
|
||||
steps:
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
platforms: linux/arm64,linux/amd64
|
||||
push: true
|
||||
|
|
@ -38,13 +38,13 @@ jobs:
|
|||
if: github.event_name == 'release'
|
||||
steps:
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
print(f"tag_name={ref_tag}", file=f)
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
platforms: linux/arm64,linux/amd64
|
||||
push: true
|
||||
|
|
|
|||
69
.github/workflows/python-publish.yml
vendored
69
.github/workflows/python-publish.yml
vendored
|
|
@ -6,65 +6,42 @@ on:
|
|||
|
||||
jobs:
|
||||
release:
|
||||
name: "Publish release"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
environment: release
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.11
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: python -m pip install --upgrade poetry
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential python3-dev libcairo2-dev libpango1.0-dev
|
||||
|
||||
# TODO: Set PYPI_API_TOKEN to api token from pip in secrets
|
||||
- name: Configure pypi credentials
|
||||
env:
|
||||
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
run: poetry config http-basic.pypi __token__ "$PYPI_API_TOKEN"
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.13
|
||||
|
||||
- name: Publish release to pypi
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Build and push release to PyPI
|
||||
run: |
|
||||
poetry publish --build
|
||||
poetry build
|
||||
uv build
|
||||
uv publish
|
||||
|
||||
- name: Store artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: dist/*.tar.gz
|
||||
name: manim.tar.gz
|
||||
- name: Install Dependency
|
||||
run: pip install requests
|
||||
- name: Get Upload URL
|
||||
id: create_release
|
||||
shell: python
|
||||
env:
|
||||
access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag_act: ${{ github.ref }}
|
||||
run: |
|
||||
import requests
|
||||
import os
|
||||
ref_tag = os.getenv('tag_act').split('/')[-1]
|
||||
access_token = os.getenv('access_token')
|
||||
headers = {
|
||||
"Accept":"application/vnd.github.v3+json",
|
||||
"Authorization": f"token {access_token}"
|
||||
}
|
||||
url = f"https://api.github.com/repos/ManimCommunity/manim/releases/tags/{ref_tag}"
|
||||
c = requests.get(url,headers=headers)
|
||||
upload_url=c.json()['upload_url']
|
||||
with open(os.getenv('GITHUB_OUTPUT'), 'w') as f:
|
||||
print(f"upload_url={upload_url}", file=f)
|
||||
print(f"tag_name={ref_tag[1:]}", file=f)
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release
|
||||
uses: actions/upload-release-asset@v1
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: dist/manim-${{ steps.create_release.outputs.tag_name }}.tar.gz
|
||||
asset_name: manim-${{ steps.create_release.outputs.tag_name }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
run: |
|
||||
TAG=${{ github.event.release.tag_name }}
|
||||
gh release upload "$TAG" "dist/manim-${TAG#v}.tar.gz"
|
||||
|
|
|
|||
|
|
@ -9,40 +9,43 @@ jobs:
|
|||
build-and-publish-htmldocs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.13
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt update && sudo apt install -y \
|
||||
pkg-config libcairo-dev libpango1.0-dev ffmpeg wget fonts-roboto
|
||||
pkg-config libcairo-dev libpango1.0-dev wget fonts-roboto
|
||||
wget -qO- "https://yihui.org/tinytex/install-bin-unix.sh" | sh
|
||||
echo ${HOME}/.TinyTeX/bin/x86_64-linux >> $GITHUB_PATH
|
||||
|
||||
- name: Install LaTeX and Python dependencies
|
||||
run: |
|
||||
tlmgr update --self
|
||||
tlmgr install \
|
||||
babel-english ctex doublestroke dvisvgm frcursive fundus-calligra jknapltx \
|
||||
mathastext microtype physics preview ragged2e relsize rsfs setspace standalone \
|
||||
wasy wasysym
|
||||
python -m pip install --upgrade poetry
|
||||
poetry install
|
||||
uv sync --extra typst
|
||||
|
||||
- name: Build and package documentation
|
||||
run: |
|
||||
cd docs/
|
||||
poetry run make html
|
||||
uv run make html
|
||||
cd build/html/
|
||||
tar -czvf ../html-docs.tar.gz *
|
||||
|
||||
- name: Store artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: ${{ github.workspace }}/manim/docs/build/html-docs.tar.gz
|
||||
path: ${{ github.workspace }}/docs/build/html-docs.tar.gz
|
||||
name: html-docs.tar.gz
|
||||
|
||||
- name: Install Dependency
|
||||
|
|
@ -79,6 +82,6 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ${{ github.workspace }}/manim/docs/build/html-docs.tar.gz
|
||||
asset_path: ${{ github.workspace }}/docs/build/html-docs.tar.gz
|
||||
asset_name: manim-htmldocs-${{ steps.create_release.outputs.tag_name }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
default_stages: [commit, push]
|
||||
default_stages: [pre-commit, pre-push]
|
||||
fail_fast: false
|
||||
exclude: ^(manim/grpc/gen/|docs/i18n/)
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
name: Validate Python
|
||||
|
|
@ -11,54 +11,27 @@ repos:
|
|||
- id: mixed-line-ending
|
||||
- id: end-of-file-fixer
|
||||
- id: check-toml
|
||||
name: Validate Poetry
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.12.0
|
||||
name: Validate pyproject.toml
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.4.1
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
- id: isort
|
||||
name: isort (cython)
|
||||
types: [cython]
|
||||
- id: isort
|
||||
name: isort (pyi)
|
||||
types: [pyi]
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.10.1
|
||||
- id: codespell
|
||||
files: ^.*\.(py|md|rst)$
|
||||
args: ["-L", "medias,nam"]
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.14.10
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
name: Update code to new python versions
|
||||
args: [--py37-plus]
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: v1.10.0
|
||||
hooks:
|
||||
- id: python-check-blanket-noqa
|
||||
name: Precision flake ignores
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.7.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/asottile/blacken-docs
|
||||
rev: 1.15.0
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies: [black==22.3.0]
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
[
|
||||
flake8-bugbear==21.4.3,
|
||||
flake8-builtins==1.5.3,
|
||||
flake8-comprehensions>=3.6.1,
|
||||
flake8-docstrings==1.6.0,
|
||||
flake8-pytest-style==1.5.0,
|
||||
flake8-rst-docstrings==0.2.3,
|
||||
flake8-simplify==0.14.1,
|
||||
]
|
||||
- id: ruff
|
||||
name: ruff lint
|
||||
types: [python]
|
||||
args: [--exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
types: [python]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v1.5.1
|
||||
rev: v1.19.1
|
||||
hooks:
|
||||
- id: mypy
|
||||
additional_dependencies:
|
||||
|
|
@ -70,10 +43,3 @@ repos:
|
|||
types-setuptools,
|
||||
]
|
||||
files: ^manim/
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.5
|
||||
hooks:
|
||||
- id: codespell
|
||||
files: ^.*\.(py|md|rst)$
|
||||
args: ["-L", "medias,nam"]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
version: 2
|
||||
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
|
||||
tools:
|
||||
python: "3.11"
|
||||
python: "3.13"
|
||||
|
||||
apt_packages:
|
||||
- libpango1.0-dev
|
||||
- ffmpeg
|
||||
- graphviz
|
||||
|
||||
python:
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ authors:
|
|||
-
|
||||
name: "The Manim Community Developers"
|
||||
cff-version: "1.2.0"
|
||||
date-released: 2023-11-11
|
||||
date-released: 2026-02-27
|
||||
license: MIT
|
||||
message: "We acknowledge the importance of good software to support research, and we note that research becomes more valuable when it is communicated effectively. To demonstrate the value of Manim, we ask that you cite Manim in your work."
|
||||
title: Manim – Mathematical Animation Framework
|
||||
url: "https://www.manim.community/"
|
||||
version: "v0.18.0"
|
||||
version: "v0.20.1"
|
||||
...
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ Examples of conflicts of interest include:
|
|||
* The reporter or reported person is a maintainer who regularly reviews your contributions
|
||||
* The reporter or reported person is your metamour.
|
||||
* The reporter or reported person is your family member
|
||||
|
||||
Committee members do not need to state why they have a conflict of interest, only that one exists. Other team members should not ask why the person has a conflict of interest.
|
||||
|
||||
Anyone who has a conflict of interest will remove themselves from the discussion of the incident, and recluse themselves from voting on a response to the report.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021, the Manim Community Developers
|
||||
Copyright (c) 2024, the Manim Community Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
40
README.md
40
README.md
|
|
@ -1,17 +1,15 @@
|
|||
<p align="center">
|
||||
<a href="https://www.manim.community/"><img src="https://raw.githubusercontent.com/ManimCommunity/manim/main/logo/cropped.png"></a>
|
||||
<a href="https://www.manim.community/"><img src="https://raw.githubusercontent.com/ManimCommunity/manim/main/logo/cropped.png" alt="Manim Community logo"></a>
|
||||
<br />
|
||||
<br />
|
||||
<a href="https://pypi.org/project/manim/"><img src="https://img.shields.io/pypi/v/manim.svg?style=flat&logo=pypi" alt="PyPI Latest Release"></a>
|
||||
<a href="https://hub.docker.com/r/manimcommunity/manim"><img src="https://img.shields.io/docker/v/manimcommunity/manim?color=%23099cec&label=docker%20image&logo=docker" alt="Docker image"> </a>
|
||||
<a href="https://mybinder.org/v2/gh/ManimCommunity/jupyter_examples/HEAD?filepath=basic_example_scenes.ipynb"><img src="https://mybinder.org/badge_logo.svg"></a>
|
||||
<a href="https://mybinder.org/v2/gh/ManimCommunity/jupyter_examples/HEAD?filepath=basic_example_scenes.ipynb"><img src="https://mybinder.org/badge_logo.svg" alt="Launch Binder"></a>
|
||||
<a href="http://choosealicense.com/licenses/mit/"><img src="https://img.shields.io/badge/license-MIT-red.svg?style=flat" alt="MIT License"></a>
|
||||
<a href="https://www.reddit.com/r/manim/"><img src="https://img.shields.io/reddit/subreddit-subscribers/manim.svg?color=orange&label=reddit&logo=reddit" alt="Reddit" href=></a>
|
||||
<a href="https://twitter.com/manim_community/"><img src="https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40manim_community" alt="Twitter">
|
||||
<a href="https://www.manim.community/discord/"><img src="https://img.shields.io/discord/581738731934056449.svg?label=discord&color=yellow&logo=discord" alt="Discord"></a>
|
||||
<a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code style: black">
|
||||
<a href="https://twitter.com/manimcommunity/"><img src="https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Follow%20%40manimcommunity" alt="Twitter">
|
||||
<a href="https://manim.community/discord/"><img src="https://img.shields.io/discord/581738731934056449.svg?label=discord&color=yellow&logo=discord" alt="Discord"></a>
|
||||
<a href="https://docs.manim.community/"><img src="https://readthedocs.org/projects/manimce/badge/?version=latest" alt="Documentation Status"></a>
|
||||
<a href="https://pepy.tech/project/manim"><img src="https://pepy.tech/badge/manim/month?" alt="Downloads"> </a>
|
||||
<img src="https://github.com/ManimCommunity/manim/workflows/CI/badge.svg" alt="CI">
|
||||
<br />
|
||||
<br />
|
||||
|
|
@ -21,21 +19,23 @@
|
|||
|
||||
Manim is an animation engine for explanatory math videos. It's used to create precise animations programmatically, as demonstrated in the videos of [3Blue1Brown](https://www.3blue1brown.com/).
|
||||
|
||||
> NOTE: This repository is maintained by the Manim Community and is not associated with Grant Sanderson or 3Blue1Brown in any way (although we are definitely indebted to him for providing his work to the world). If you would like to study how Grant makes his videos, head over to his repository ([3b1b/manim](https://github.com/3b1b/manim)). This fork is updated more frequently than his, and it's recommended to use this fork if you'd like to use Manim for your own projects.
|
||||
> [!NOTE]
|
||||
> The community edition of Manim (ManimCE) is a version maintained and developed by the community. It was forked from 3b1b/manim, a tool originally created and open-sourced by Grant Sanderson, also creator of the 3Blue1Brown educational math videos. While Grant Sanderson continues to maintain his own repository, we recommend this version for its continued development, improved features, enhanced documentation, and more active community-driven maintenance. If you would like to study how Grant makes his videos, head over to his repository ([3b1b/manim](https://github.com/3b1b/manim)).
|
||||
|
||||
## Table of Contents:
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Documentation](#documentation)
|
||||
- [Docker](#docker)
|
||||
- [Help with Manim](#help-with-manim)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Documentation](#documentation)
|
||||
- [Docker](#docker)
|
||||
- [Help with Manim](#help-with-manim)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
> **WARNING:** These instructions are for the community version _only_. Trying to use these instructions to install [3b1b/manim](https://github.com/3b1b/manim) or instructions there to install this version will cause problems. Read [this](https://docs.manim.community/en/stable/faq/installation.html#why-are-there-different-versions-of-manim) and decide which version you wish to install, then only follow the instructions for your desired version.
|
||||
> [!CAUTION]
|
||||
> These instructions are for the community version _only_. Trying to use these instructions to install [3b1b/manim](https://github.com/3b1b/manim) or instructions there to install this version will cause problems. Read [this](https://docs.manim.community/en/stable/faq/installation.html#why-are-there-different-versions-of-manim) and decide which version you wish to install, then only follow the instructions for your desired version.
|
||||
|
||||
Manim requires a few dependencies that must be installed prior to using it. If you
|
||||
want to try it out first before installing it locally, you can do so
|
||||
|
|
@ -88,9 +88,9 @@ The `-p` flag in the command above is for previewing, meaning the video file wil
|
|||
|
||||
Some other useful flags include:
|
||||
|
||||
- `-s` to skip to the end and just show the final frame.
|
||||
- `-n <number>` to skip ahead to the `n`'th animation of a scene.
|
||||
- `-f` show the file in the file browser.
|
||||
- `-s` to skip to the end and just show the final frame.
|
||||
- `-n <number>` to skip ahead to the `n`'th animation of a scene.
|
||||
- `-f` show the file in the file browser.
|
||||
|
||||
For a thorough list of command line arguments, visit the [documentation](https://docs.manim.community/en/stable/guides/configuration.html).
|
||||
|
||||
|
|
@ -118,8 +118,8 @@ The contribution guide may become outdated quickly; we highly recommend joining
|
|||
[Discord server](https://www.manim.community/discord/) to discuss any potential
|
||||
contributions and keep up to date with the latest developments.
|
||||
|
||||
Most developers on the project use `poetry` for management. You'll want to have poetry installed and available in your environment.
|
||||
Learn more about `poetry` at its [documentation](https://python-poetry.org/docs/) and find out how to install manim with poetry at the [manim dev-installation guide](https://docs.manim.community/en/stable/contributing/development.html) in the manim documentation.
|
||||
Most developers on the project use `uv` for management. You'll want to have uv installed and available in your environment.
|
||||
Learn more about `uv` at its [documentation](https://docs.astral.sh/uv/) and find out how to install manim with uv at the [manim dev-installation guide](https://docs.manim.community/en/latest/contributing/development.html) in the manim documentation.
|
||||
|
||||
## How to Cite Manim
|
||||
|
||||
|
|
|
|||
382
agents/typst_selector.md
Normal file
382
agents/typst_selector.md
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
# Design: Sub-Expression Selection for `Typst` / `TypstMath`
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Users need to interact with individual parts of a Typst-rendered expression:
|
||||
color a variable, animate the numerator of a fraction, morph one sub-expression
|
||||
into another, etc. The `MathTex` class solves this with:
|
||||
|
||||
1. **`{{ ... }}` double-brace notation** — splits the TeX string into named
|
||||
submobject groups at compile time.
|
||||
2. **`substrings_to_isolate` / `get_part_by_tex`** — identifies submobjects
|
||||
whose TeX source matches a given string.
|
||||
|
||||
Both mechanisms ultimately rely on injecting `\special{dvisvgm:raw <g id='...'>}`
|
||||
markers into the LaTeX source so that the resulting SVG contains `<g>` elements
|
||||
with known `id` attributes, which SVGMobject's parser maps to `VGroup`
|
||||
sub-trees via `id_to_vgroup_dict`.
|
||||
|
||||
We need an analogous mechanism for Typst.
|
||||
|
||||
## Key Discovery: `data-typst-label` in SVG Output
|
||||
|
||||
Typst's SVG renderer (`typst-svg` crate) already emits a `data-typst-label`
|
||||
attribute on `<g>` elements whenever a `GroupItem` (hard frame) carries a
|
||||
label. The relevant code path:
|
||||
|
||||
```rust
|
||||
// typst-svg/src/lib.rs — render_group()
|
||||
if let Some(label) = group.label {
|
||||
svg.init().attr("data-typst-label", label.resolve());
|
||||
}
|
||||
```
|
||||
|
||||
A **hard frame** is created by the `box` element (and `block`, etc.). Crucially,
|
||||
`box` can be used inline inside math mode, and labels can be attached to it.
|
||||
|
||||
### Proof of Concept
|
||||
|
||||
The following Typst helper wraps content in a labeled `box`:
|
||||
|
||||
```typst
|
||||
#let grp(lbl, body) = [#box(body) #label(lbl)]
|
||||
```
|
||||
|
||||
When used in math:
|
||||
|
||||
```typst
|
||||
$ #grp("numerator", $a + b$) / #grp("denom", $c - d$) = #grp("result", $x$) $
|
||||
```
|
||||
|
||||
The compiled SVG contains:
|
||||
|
||||
```xml
|
||||
<g class="typst-group" ... data-typst-label="numerator">
|
||||
<!-- glyphs for a + b -->
|
||||
</g>
|
||||
<g class="typst-group" ... data-typst-label="denom">
|
||||
<!-- glyphs for c - d -->
|
||||
</g>
|
||||
<g class="typst-group" ... data-typst-label="result">
|
||||
<!-- glyph for x -->
|
||||
</g>
|
||||
```
|
||||
|
||||
**Nesting works.** A `grp` wrapping a fraction that itself contains `grp`-ed
|
||||
sub-parts produces nested `data-typst-label` groups:
|
||||
|
||||
```typst
|
||||
$ #grp("whole-frac", $frac(#grp("numerator", $a + b$), #grp("denom", $c - d$))$) $
|
||||
```
|
||||
|
||||
SVG output:
|
||||
|
||||
```xml
|
||||
<g ... data-typst-label="whole-frac">
|
||||
<g ... data-typst-label="numerator"> ... </g>
|
||||
<g ... data-typst-label="denom"> ... </g>
|
||||
<path class="typst-shape" ... /> <!-- fraction bar -->
|
||||
</g>
|
||||
```
|
||||
|
||||
### SVG Parser Compatibility
|
||||
|
||||
Manim uses `svgelements` to parse SVGs. The library preserves
|
||||
`data-typst-label` in the `values` dictionary of `Group` objects, and it
|
||||
propagates to child elements. Manim's `SVGMobject.get_mobjects_from()` already
|
||||
iterates over groups and builds `id_to_vgroup_dict` keyed by the `id` attribute.
|
||||
Extending this to also key by `data-typst-label` is straightforward.
|
||||
|
||||
## Proposed Interface
|
||||
|
||||
### 1. Explicit Groups via `{{ ... }}` Notation (Compile-Time)
|
||||
|
||||
Mirror the `MathTex` double-brace convention. Users write:
|
||||
|
||||
```python
|
||||
eq = TypstMath("{{ a + b }} / {{ c - d }} = {{ x }}")
|
||||
```
|
||||
|
||||
The pre-processor splits on `{{ ... }}` (reusing the same whitespace-guard
|
||||
rules as `MathTex._split_double_braces`) and wraps each group in a labeled
|
||||
`box` call:
|
||||
|
||||
```typst
|
||||
$ #box[$a + b$] <_grp-0> / #box[$c - d$] <_grp-1> = #box[$x$] <_grp-2> $
|
||||
```
|
||||
|
||||
Each group gets an auto-generated label (`_grp-0`, `_grp-1`, ...).
|
||||
The `data-typst-label` attributes then appear in the SVG, and
|
||||
`SVGMobject.get_mobjects_from()` can map them to `VGroup` entries in
|
||||
`label_to_vgroup_dict` (or reuse `id_to_vgroup_dict`).
|
||||
|
||||
These groups become sub-mobjects of the `TypstMath` instance, accessible by
|
||||
index:
|
||||
|
||||
```python
|
||||
eq[0] # VGroup for "a + b"
|
||||
eq[1] # VGroup for "c - d"
|
||||
eq[2] # VGroup for "x"
|
||||
```
|
||||
|
||||
(Non-group content between groups — like `/` and `=` — also becomes
|
||||
its own submobject, mirroring `MathTex` behavior.)
|
||||
|
||||
**For `Typst` (text mode):** the same `{{ ... }}` notation applies, but the
|
||||
wrapper is `#box[...]` without math delimiters.
|
||||
|
||||
### 2. Named Groups via Labels
|
||||
|
||||
Users can also assign explicit label names for retrieval by name:
|
||||
|
||||
```python
|
||||
eq = Typst(
|
||||
r"$ #box[$a + b$] <numerator> / #box[$c - d$] <denom> $"
|
||||
)
|
||||
eq.select("numerator").set_color(RED)
|
||||
eq.select("denom").set_color(BLUE)
|
||||
```
|
||||
|
||||
Alternatively, an even more ergonomic approach that hides the `box` boilerplate
|
||||
and uses the `{{ ... : label }}` notation:
|
||||
|
||||
```python
|
||||
eq = TypstMath("{{ a + b : numerator }} / {{ c - d : denom }}")
|
||||
eq.select("numerator").set_color(RED)
|
||||
```
|
||||
|
||||
Here the pre-processor recognizes `{{ content : label }}` and emits
|
||||
`#box[$content$] <label>` in the Typst source.
|
||||
|
||||
### 3. The `.select()` Method
|
||||
|
||||
```python
|
||||
def select(self, key: str | int) -> VGroup:
|
||||
"""Select a labeled sub-expression.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
key
|
||||
Either a label name (string) matching a ``data-typst-label``
|
||||
in the SVG, or an integer index into the auto-numbered
|
||||
``{{ ... }}`` groups.
|
||||
|
||||
Returns
|
||||
-------
|
||||
VGroup
|
||||
The sub-mobjects corresponding to the selected group.
|
||||
|
||||
Raises
|
||||
------
|
||||
KeyError
|
||||
If no group with the given label/index exists.
|
||||
"""
|
||||
```
|
||||
|
||||
This returns a `VGroup` containing exactly the submobjects (paths) that
|
||||
were rendered inside the corresponding `<g data-typst-label="...">` in the SVG.
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Step 1: Extend `SVGMobject.get_mobjects_from()` to Track Labels
|
||||
|
||||
In `manim/mobject/svg/svg_mobject.py`, the group-walking loop already checks
|
||||
for `id` attributes. Add a parallel check for `data-typst-label`:
|
||||
|
||||
```python
|
||||
try:
|
||||
group_name = str(element.values["id"])
|
||||
except Exception:
|
||||
# Fall back to data-typst-label if available
|
||||
label = element.values.get("data-typst-label")
|
||||
if label:
|
||||
group_name = f"typst-label:{label}"
|
||||
else:
|
||||
group_name = f"numbered_group_{group_id_number}"
|
||||
group_id_number += 1
|
||||
```
|
||||
|
||||
This automatically populates `id_to_vgroup_dict` with label-keyed entries.
|
||||
|
||||
### Step 2: Pre-Processing `{{ ... }}` in Typst Source
|
||||
|
||||
Add a `_split_and_label_groups()` method that:
|
||||
|
||||
1. Scans the input for `{{ ... }}` or `{{ ... : label }}` patterns
|
||||
(using the same whitespace-guard rules as `MathTex._split_double_braces`).
|
||||
2. Replaces each group with `#box[$content$] <label>` (math mode) or
|
||||
`#box[content] <label>` (text mode).
|
||||
3. Records the mapping from label → original source string for later lookup.
|
||||
|
||||
### Step 3: `Typst.select()` / Index Access
|
||||
|
||||
- Store the ordered list of group labels and their source strings.
|
||||
- `select(label_or_index)` looks up the corresponding `VGroup` from
|
||||
`id_to_vgroup_dict` (using the `typst-label:...` key).
|
||||
- `__getitem__(int)` returns the *n*-th group's `VGroup`.
|
||||
|
||||
### Step 4: Compatibility with `TransformMatchingTex` (future)
|
||||
|
||||
`TransformMatchingTex` (and its successor `TransformMatchingShapes`) works by
|
||||
matching submobjects between two `MathTex` instances by their TeX string keys.
|
||||
The same approach extends to `Typst` if each `{{ ... }}` group carries its
|
||||
original source string as metadata. A `TransformMatchingTypst` animation could
|
||||
match groups by label name or by source string equality.
|
||||
|
||||
## Open Design Questions
|
||||
|
||||
### Q1: Context-Aware Wrapping — Math vs. Text Mode
|
||||
|
||||
The `box` + `label` mechanism works identically in math and text mode, but the
|
||||
**wrapping** of group content must match the surrounding context:
|
||||
|
||||
- **In text mode:** `{{ Hello : greeting }}` → `#box[Hello] <greeting>`
|
||||
- **In math mode:** `{{ y^2 : second }}` → `#box[$y^2$] <second>`
|
||||
|
||||
Getting this wrong is not a silent error — it produces visually broken output.
|
||||
Wrapping math content with `#box[y^2]` (no `$...$`) renders `y^2` as literal
|
||||
text in the body font instead of as a math superscript.
|
||||
|
||||
This is a real problem for `Typst()`, where a single source string can mix text
|
||||
and math freely:
|
||||
|
||||
```python
|
||||
Typst("hello world, here is a formula: $x^2 + {{ y^2 : second }} = z^2$")
|
||||
```
|
||||
|
||||
Here `{{ y^2 : second }}` is inside a `$ ... $` block, so it needs the
|
||||
math-mode wrapper, but the pre-processor has no way to know this unless it
|
||||
tracks `$` delimiters.
|
||||
|
||||
### The `#` prefix problem and math calls
|
||||
|
||||
A natural idea is to translate `{{ content }}` into a Typst function call like
|
||||
`grp("lbl", content)`. However, this has a subtle but critical context
|
||||
sensitivity: Typst has two different call conventions depending on context:
|
||||
|
||||
- **Math call** (no `#` prefix): `$ grp("lbl", a^2 + b) $` — arguments are
|
||||
parsed **in math mode**. The content `a^2 + b` is math. ✓
|
||||
- **Code call** (`#` prefix): `$ #grp("lbl", a^2 + b) $` — arguments are
|
||||
parsed **in code mode**. `a^2` is a syntax error in code! ✗
|
||||
|
||||
So in math mode, the function MUST be called without `#` for args to stay in
|
||||
math mode. In text/markup mode, the function MUST be called WITH `#` (that's
|
||||
how you invoke code from markup), and content arguments need `[...]` wrapping:
|
||||
|
||||
```typst
|
||||
// Text context: #grp("lbl", [Hello world])
|
||||
// Math context: grp("lbl", a^2 + b)
|
||||
```
|
||||
|
||||
The function definition is the same either way:
|
||||
```typst
|
||||
#let grp(lbl, body) = [#box(body) #label(lbl)]
|
||||
```
|
||||
|
||||
This means the function call approach has **exactly the same context problem**
|
||||
as the raw `#box` approach: the pre-processor must know whether it's in math or
|
||||
text to emit the right calling convention.
|
||||
|
||||
### Further complication: string literals and content blocks
|
||||
|
||||
Even inside `TypstMath` (where everything is math), the scanner must avoid
|
||||
`{{ }}` matches inside string literals or content blocks:
|
||||
|
||||
```python
|
||||
TypstMath('x^2 + y^2 =_("Hello {{ world }}") z^2')
|
||||
```
|
||||
|
||||
Here `{{ world }}` is inside a `"..."` string literal — it should NOT be
|
||||
processed. Similarly, content blocks `[...]` inside math switch back to text
|
||||
mode.
|
||||
|
||||
### Options
|
||||
|
||||
**A. `TypstMath`: math calls with simple string-aware scanning.**
|
||||
For `TypstMath`, the entire body is math, so `{{ content }}` always becomes
|
||||
`grp("_grp-N", content)` (no `#`, no `$...$`). The scanner just needs to
|
||||
skip `"..."` string literals and `[...]` content blocks — no `$` tracking
|
||||
needed. This is clean and robust.
|
||||
|
||||
**B. `Typst`: context-aware scanning (full parser).**
|
||||
For the general `Typst` class, the scanner must additionally track `$...$`
|
||||
math blocks (toggling a mode flag on unescaped `$`) to choose between
|
||||
`grp(...)` (in math) and `#grp("lbl", [...])` (in text). It must also handle
|
||||
string literals and content blocks inside math that switch context back. This
|
||||
is doable but non-trivial — essentially a mini Typst lexer.
|
||||
|
||||
**C. `Typst`: no `{{ }}`, manual groups only.**
|
||||
For the general `Typst` class, don't support `{{ }}` at all. Users write
|
||||
`grp(...)` / `#grp(...)` themselves (with the helper injected into the
|
||||
preamble). `{{ }}` is only available on `TypstMath`. This is simpler and
|
||||
avoids the parsing complexity, at the cost of ergonomics for mixed-mode
|
||||
documents.
|
||||
|
||||
**Recommendation:** Start with A (TypstMath only) and C (manual for Typst).
|
||||
Upgrade to B later if demand warrants it — the function call infrastructure
|
||||
is already in place, it's only the scanner that needs upgrading.
|
||||
|
||||
### Q2: What about "unlabeled" content between groups?
|
||||
|
||||
Like `MathTex`, the pieces of content *between* `{{ ... }}` groups should also
|
||||
become their own submobjects (auto-labeled with sequential indices). For
|
||||
example:
|
||||
|
||||
```python
|
||||
TypstMath("{{ a }} + {{ b }} = {{ c }}")
|
||||
# group-0: "a"
|
||||
# group-1: "+" (auto-group for inter-group content)
|
||||
# group-2: "b"
|
||||
# group-3: "=" (auto-group for inter-group content)
|
||||
# group-4: "c"
|
||||
```
|
||||
|
||||
Each segment (group or inter-group) gets wrapped in its own labeled `box`.
|
||||
|
||||
### Q3: What happens with `box` and baseline alignment?
|
||||
|
||||
`box` is an inline element in Typst, and when used inside math mode it
|
||||
participates in math layout. Testing confirms that fractions, superscripts, and
|
||||
other constructs render correctly when their children are `box`-wrapped.
|
||||
However, `box` creates a "hard frame" boundary which may subtly affect spacing
|
||||
in edge cases (e.g., math operator spacing around a boxed expression). This
|
||||
needs further testing; if issues arise, we could explore `block(breakable: false)`
|
||||
or invisible `rect` wrappers as alternatives.
|
||||
|
||||
### Q4: Can we avoid the `#grp(...)` / `#box[...] <label>` verbosity?
|
||||
|
||||
Yes — the `{{ ... }}` double-brace notation is purely syntactic sugar that gets
|
||||
pre-processed by Manim before the source reaches the Typst compiler. Users never
|
||||
need to write raw `#box` or `#label()` calls unless they want finer control.
|
||||
|
||||
### Q5: String-based selection without explicit groups?
|
||||
|
||||
A future enhancement could support:
|
||||
|
||||
```python
|
||||
eq = TypstMath(r"a + b = c")
|
||||
eq.select("a") # finds submobjects corresponding to the glyph "a"
|
||||
```
|
||||
|
||||
This is hard to do reliably because:
|
||||
- Typst SVGs embed glyphs as `<use xlink:href="#gXXX">` references; there's no
|
||||
text content in the SVG itself.
|
||||
- A single variable in Typst may span multiple glyphs (e.g., `"alpha"` → one
|
||||
glyph) or identical glyphs may appear multiple times.
|
||||
|
||||
A possible approach: at pre-processing time, wrap every "token" in the Typst
|
||||
math source in its own labeled `box`. This would require a Typst math tokenizer
|
||||
and is better suited for a v2 implementation.
|
||||
|
||||
## Summary: What Typst Gives Us
|
||||
|
||||
| Mechanism | How it works | SVG output |
|
||||
|---|---|---|
|
||||
| `#box(body) <label>` | Creates a hard-frame `GroupItem` with a `Label` | `<g data-typst-label="label">...</g>` |
|
||||
| `#metadata(val) <label>` | Invisible; queryable via `typst query` CLI | No visual output (useful for CLI queries, not SVG) |
|
||||
| Show rules on labels | `#show <label>: ...` | Transforms visual output but no automatic SVG grouping |
|
||||
| `context query(<label>)` | Document introspection (positions, counters) | In-document only; not available from Python |
|
||||
|
||||
The `box` + `label` mechanism is the **only** one that produces identifiable
|
||||
groups in the SVG output, making it the correct tool for sub-expression
|
||||
selection in Manim.
|
||||
49
conftest.py
49
conftest.py
|
|
@ -1,49 +0,0 @@
|
|||
# This file is automatically picked by pytest
|
||||
# while running tests. So, that each test is
|
||||
# run on difference temporary directories and avoiding
|
||||
# errors.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
try:
|
||||
# https://github.com/moderngl/moderngl/issues/517
|
||||
import readline # required to prevent a segfault on Python 3.10
|
||||
except ModuleNotFoundError: # windows
|
||||
pass
|
||||
|
||||
import cairo
|
||||
import moderngl
|
||||
|
||||
# If it is running Doctest the current directory
|
||||
# is changed because it also tests the config module
|
||||
# itself. If it's a normal test then it uses the
|
||||
# tempconfig to change directories.
|
||||
import pytest
|
||||
from _pytest.doctest import DoctestItem
|
||||
|
||||
from manim import config, tempconfig
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def temp_media_dir(tmpdir, monkeypatch, request):
|
||||
if isinstance(request.node, DoctestItem):
|
||||
monkeypatch.chdir(tmpdir)
|
||||
yield tmpdir
|
||||
else:
|
||||
with tempconfig({"media_dir": str(tmpdir)}):
|
||||
assert config.media_dir == str(tmpdir)
|
||||
yield tmpdir
|
||||
|
||||
|
||||
def pytest_report_header(config):
|
||||
ctx = moderngl.create_standalone_context()
|
||||
info = ctx.info
|
||||
ctx.release()
|
||||
return (
|
||||
f"\nCairo Version: {cairo.cairo_version()}",
|
||||
"\nOpenGL information",
|
||||
"------------------",
|
||||
f"vendor: {info['GL_VENDOR'].strip()}",
|
||||
f"renderer: {info['GL_RENDERER'].strip()}",
|
||||
f"version: {info['GL_VERSION'].strip()}\n",
|
||||
)
|
||||
|
|
@ -1,56 +1,88 @@
|
|||
FROM python:3.8-slim
|
||||
# ── Stage 1: builder ─────────────────────────────────────────────────────────
|
||||
FROM python:3.14-slim AS builder
|
||||
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
ffmpeg \
|
||||
build-essential \
|
||||
gcc \
|
||||
cmake \
|
||||
make \
|
||||
pkg-config \
|
||||
wget \
|
||||
libcairo2-dev \
|
||||
libffi-dev \
|
||||
libpango1.0-dev \
|
||||
freeglut3-dev \
|
||||
pkg-config \
|
||||
make \
|
||||
wget \
|
||||
ghostscript
|
||||
libegl-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# setup a minimal texlive installation
|
||||
# Setup a minimal TeX Live installation (no ctex: drops ~100 MB of CJK fonts/packages)
|
||||
COPY docker/texlive-profile.txt /tmp/
|
||||
ENV PATH=/usr/local/texlive/bin/armhf-linux:/usr/local/texlive/bin/aarch64-linux:/usr/local/texlive/bin/x86_64-linux:$PATH
|
||||
RUN wget -O /tmp/install-tl-unx.tar.gz http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz && \
|
||||
mkdir /tmp/install-tl && \
|
||||
tar -xzf /tmp/install-tl-unx.tar.gz -C /tmp/install-tl --strip-components=1 && \
|
||||
/tmp/install-tl/install-tl --profile=/tmp/texlive-profile.txt \
|
||||
RUN wget -O /tmp/install-tl-unx.tar.gz http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz \
|
||||
&& mkdir /tmp/install-tl \
|
||||
&& tar -xzf /tmp/install-tl-unx.tar.gz -C /tmp/install-tl --strip-components=1 \
|
||||
&& /tmp/install-tl/install-tl --profile=/tmp/texlive-profile.txt \
|
||||
&& tlmgr install \
|
||||
amsmath babel-english cbfonts-fd cm-super ctex doublestroke dvisvgm everysel \
|
||||
amsmath babel-english cbfonts-fd cm-super count1to doublestroke dvisvgm everysel \
|
||||
fontspec frcursive fundus-calligra gnu-freefont jknapltx latex-bin \
|
||||
mathastext microtype ms physics preview ragged2e relsize rsfs \
|
||||
setspace standalone tipa wasy wasysym xcolor xetex xkeyval
|
||||
mathastext microtype multitoc physics prelim2e preview ragged2e relsize rsfs \
|
||||
setspace standalone tipa wasy wasysym xcolor xetex xkeyval \
|
||||
&& rm -rf /tmp/install-tl /tmp/install-tl-unx.tar.gz
|
||||
|
||||
# Install manim into an isolated virtualenv
|
||||
ENV VIRTUAL_ENV=/opt/venv
|
||||
RUN python -m venv $VIRTUAL_ENV
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
# clone and build manim
|
||||
COPY . /opt/manim
|
||||
WORKDIR /opt/manim
|
||||
RUN pip install --no-cache .[jupyterlab]
|
||||
RUN pip install --no-cache-dir .[jupyterlab]
|
||||
|
||||
RUN pip install -r docs/requirements.txt
|
||||
# ── Stage 2: runtime ─────────────────────────────────────────────────────────
|
||||
FROM python:3.14-slim
|
||||
|
||||
# Runtime libs only:
|
||||
# - no ffmpeg: PyAV (av package) bundles its own ffmpeg libraries in av.libs/
|
||||
# - OpenGL: keep EGL for headless rendering and libGL as required by moderngl/glcontext
|
||||
# - fonts-noto-core instead of fonts-noto (drops CJK noto fonts)
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
libcairo2 \
|
||||
libpango-1.0-0 \
|
||||
libpangocairo-1.0-0 \
|
||||
libpangoft2-1.0-0 \
|
||||
libffi8 \
|
||||
libegl1 \
|
||||
libgl1 \
|
||||
ghostscript \
|
||||
fonts-noto-core \
|
||||
fontconfig \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN fc-cache -fv
|
||||
|
||||
# Copy TeX Live from builder
|
||||
ENV PATH=/usr/local/texlive/bin/armhf-linux:/usr/local/texlive/bin/aarch64-linux:/usr/local/texlive/bin/x86_64-linux:$PATH
|
||||
COPY --from=builder /usr/local/texlive /usr/local/texlive
|
||||
|
||||
# Copy the pre-built virtualenv from builder
|
||||
ENV VIRTUAL_ENV=/opt/venv
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
ARG NB_USER=manimuser
|
||||
ARG NB_UID=1000
|
||||
ENV USER ${NB_USER}
|
||||
ENV NB_UID ${NB_UID}
|
||||
ENV HOME /manim
|
||||
ENV USER=${NB_USER}
|
||||
ENV NB_UID=${NB_UID}
|
||||
ENV HOME=/manim
|
||||
|
||||
RUN adduser --disabled-password \
|
||||
--gecos "Default user" \
|
||||
--uid ${NB_UID} \
|
||||
${NB_USER}
|
||||
|
||||
# create working directory for user to mount local directory into
|
||||
WORKDIR ${HOME}
|
||||
USER root
|
||||
RUN chown -R ${NB_USER}:${NB_USER} ${HOME}
|
||||
RUN chmod 777 ${HOME}
|
||||
RUN chown -R ${NB_USER}:${NB_USER} ${HOME} && chmod 777 ${HOME}
|
||||
USER ${NB_USER}
|
||||
|
||||
CMD [ "/bin/bash" ]
|
||||
CMD ["/bin/bash"]
|
||||
|
|
|
|||
|
|
@ -13,3 +13,11 @@ Multi-platform builds are possible by running
|
|||
docker buildx build --push --platform linux/arm64/v8,linux/amd64 --tag manimcommunity/manim:TAG -f docker/Dockerfile .
|
||||
```
|
||||
from the root directory of the repository.
|
||||
|
||||
# Runtime notes
|
||||
- The image is built via a multi-stage Dockerfile (build dependencies are not
|
||||
carried into the runtime stage).
|
||||
- The image does not include the `ffmpeg` CLI binary.
|
||||
- The default TeX installation is minimal and does not include `ctex`.
|
||||
- Headless OpenGL rendering relies on EGL/GL runtime libraries available in the
|
||||
image.
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ msgid "This ``Scene`` illustrates the quirks of ``.animate``. When using ``.anim
|
|||
msgstr ""
|
||||
|
||||
#: ../../source/tutorials/quickstart.rst:344
|
||||
msgid "In ``DifferentRotations``, the difference between ``.animate``'s interpretation of rotation and the ``Rotate`` method is far more apparent. The starting and ending states of a ``Mobject`` rotated 360 degrees are the same, so ``.animate`` tries to interpolate two identical objects and the result is the left square. If you find that your own usage of ``.animate`` is causing similar unwanted behavior, consider using conventional animation methods like the right square, which uses ``Rotate``."
|
||||
msgid "In ``DifferentRotations``, the difference between ``.animate``'s interpretation of rotation and the ``Rotate`` method is far more apparent. The starting and ending states of a ``Mobject`` rotated 180 degrees are the same, so ``.animate`` tries to interpolate two identical objects and the result is the left square. If you find that your own usage of ``.animate`` is causing similar unwanted behavior, consider using conventional animation methods like the right square, which uses ``Rotate``."
|
||||
msgstr ""
|
||||
|
||||
#: ../../source/tutorials/quickstart.rst:353
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
furo
|
||||
myst-parser
|
||||
sphinx<5.1
|
||||
sphinx>=7.3
|
||||
sphinx-copybutton
|
||||
sphinxext-opengraph
|
||||
sphinx-design
|
||||
sphinx-reredirects
|
||||
typst>=0.14
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
jupyterlab
|
||||
sphinxcontrib-programoutput
|
||||
typst>=0.14
|
||||
|
|
|
|||
|
|
@ -4,16 +4,9 @@
|
|||
|
||||
.. automodule:: {{ fullname }}
|
||||
|
||||
{% block attributes %}
|
||||
{% if attributes %}
|
||||
.. rubric:: Module Attributes
|
||||
|
||||
.. autosummary::
|
||||
{% for item in attributes %}
|
||||
{{ item }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{# SEE manim.utils.docbuild.autoaliasattr_directive #}
|
||||
{# FOR INFORMATION ABOUT THE CUSTOM autoaliasattr DIRECTIVE! #}
|
||||
.. autoaliasattr:: {{ fullname }}
|
||||
|
||||
{% block classes %}
|
||||
{% if classes %}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,18 @@
|
|||
Changelog
|
||||
#########
|
||||
|
||||
This page contains a list of changes made between releases.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
changelog/0.20.1-changelog
|
||||
changelog/0.20.0-changelog
|
||||
changelog/0.19.2-changelog
|
||||
changelog/0.19.1-changelog
|
||||
changelog/0.19.0-changelog
|
||||
changelog/0.18.1-changelog
|
||||
changelog/0.18.0.post0-changelog
|
||||
changelog/0.18.0-changelog
|
||||
changelog/0.17.3-changelog
|
||||
changelog/0.17.2-changelog
|
||||
|
|
|
|||
9
docs/source/changelog/0.18.0.post0-changelog.rst
Normal file
9
docs/source/changelog/0.18.0.post0-changelog.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
*************
|
||||
v0.18.0.post0
|
||||
*************
|
||||
|
||||
:Date: April 08, 2024
|
||||
|
||||
This release is a post-release fixing `#3676
|
||||
<https://github.com/ManimCommunity/manim/issues/3676>`_, a bug caused by a recent
|
||||
change introduced to the way how SVG files of text are generated by Pango.
|
||||
160
docs/source/changelog/0.18.1-changelog.md
Normal file
160
docs/source/changelog/0.18.1-changelog.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
---
|
||||
short-title: v0.18.1
|
||||
description: Changelog for Manim v0.18.1
|
||||
---
|
||||
|
||||
# v0.18.1
|
||||
|
||||
Date
|
||||
: April 28, 2024
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Breaking Changes and Deprecations
|
||||
|
||||
* Removed deprecated `manim new` command by {user}`chopan050` in {pr}`3512`
|
||||
* Removed support for dynamic plugin imports by {user}`Viicos` in {pr}`3524`
|
||||
* Remove meth:``.Mobject.wag`` by {user}`JasonGrace2282` in {pr}`3539`
|
||||
* Remove deprecated parameters and animations by {user}`JasonGrace2282` in {pr}`3688`
|
||||
|
||||
|
||||
### New Features
|
||||
|
||||
* Added `cap_style` feature to `VMobject` by {user}`MathItYT` in {pr}`3516`
|
||||
* Allow hiding version splash by {user}`jeertmans` in {pr}`3329`
|
||||
* Added the ability to pass lists and generators to `Scene.play()` by {user}`MrDiver` in {pr}`3365`
|
||||
* Added ``--preview_command`` cli flag by {user}`JasonGrace2282` in {pr}`3615`
|
||||
|
||||
### Fixed Bugs and Enhancements
|
||||
|
||||
* Allow accessing ghost vectors in :class:`.LinearTransformationScene` by {user}`JasonGrace2282` in {pr}`3435`
|
||||
* Optimized `get_unit_normal()` and replaced `np.cross()` with custom `cross()` in `manim.utils.space_ops` by {user}`chopan050` in {pr}`3494`
|
||||
* Implement caching of fonts list to improve runtime performance by {user}`MrDiver` in {pr}`3316`
|
||||
* Reformatting the `--save_sections` output to have the format `<Scene>_<SecNum>_<SecName><extension>` by {user}`doaamuham` in {pr}`3499`
|
||||
* Account for dtype in the pixel array so the maximum value stays correct in the invert function by {user}`jeertmans` in {pr}`3493`
|
||||
* Added `grid_lines` attribute to `Rectangle` to add individual styling to the grid lines by {user}`RobinPH` in {pr}`3428`
|
||||
* Fixed rectangle grid properties (#3082) by {user}`pauluhlenbruck` in {pr}`3513`
|
||||
* Fixed animations with zero runtime length to give a useful error instead of a broken pipe by {user}`MrDiver` in {pr}`3491`
|
||||
* Fixed stroke width being ignored by `StreamLines` with a single color by {user}`yashm277` in {pr}`3436`
|
||||
* Fixed formatting of ``MoveAlongPath`` docs by {user}`JasonGrace2282` in {pr}`3541`
|
||||
* Added helpful hints to `VGroup.add()` error message by {user}`vvolhejn` in {pr}`3561`
|
||||
* Made `earclip_triangulation` more robust by {user}`hydromelvictor` in {pr}`3574`
|
||||
* Refactored `TexTemplate` by {user}`Viicos` in {pr}`3520`
|
||||
* Fixed `write_subcaption_file` error when using OpenGL renderer by {user}`yuan-xy` in {pr}`3546`
|
||||
* Fixed `get_arc_center()` returning reference of point by {user}`sparshg` in {pr}`3599`
|
||||
* Improved handling of specified font name by {user}`staghado` in {pr}`3429`
|
||||
* Fixing the behavior of `.become` to not modify target mobject via side effects fix color linking by {user}`MrDiver` in {pr}`3508`
|
||||
* Fixed bug in :class:`.VMobjectFromSVGPath` by {user}`abul4fia` in {pr}`3677`
|
||||
* Fix for windows cp1252 encoding failure (fix test pipeline) by {user}`JasonGrace2282` in {pr}`3687`
|
||||
* Fix NameError in try... except by {user}`JasonGrace2282` in {pr}`3694`
|
||||
* Fix successive calls of :meth:`.LinearTransformationScene.apply_matrix` by {user}`SirJamesClarkMaxwell` in {pr}`3675`
|
||||
* Fixed `Mobject.put_start_and_end_on` with same start and end point by {user}`MontroyJosh` in {pr}`3718`
|
||||
* Fixed issue where `SpiralIn` doesn't show elements by {user}`Gixtox` in {pr}`3589`
|
||||
* Cleaned `Graph` layouts and increase flexibility by {user}`Nikhil-42` in {pr}`3434`
|
||||
* `AnimationGroup`: optimized `interpolate()` and fixed alpha bug on `finish()` by {user}`chopan050` in {pr}`3542`
|
||||
* Fixed warning about missing plugin `""` by {user}`behackl` in {pr}`3734`
|
||||
|
||||
### Documentation
|
||||
|
||||
* Typo in `indication` documentation by {user}`jcep` in {pr}`3477`
|
||||
* Fixed typo: 360° to 180° in quickstart tutorial by {user}`szchixy` in {pr}`3498`
|
||||
* Fixed typo in mobject docstring: `line` -> `square` by {user}`yuan-xy` in {pr}`3509`
|
||||
* Explain ``.Transform`` vs ``.ReplacementTransform`` in quickstart examples by {user}`JasonGrace2282` in {pr}`3500`
|
||||
* Fixed formatting in building blocks tutorial by {user}`MrDiver` in {pr}`3515`
|
||||
* Fixed `Indicate` docstring typo by {user}`Lawqup` in {pr}`3461`
|
||||
* Added Documentation to `.to_edge` and `to_corner` by {user}`TheMathematicFanatic` in {pr}`3408`
|
||||
* Added some words about Cairo 1.18 by {user}`jeertmans` in {pr}`3530`
|
||||
* Fixed typo of `get_y_axis_label` parameter documentation by {user}`yuan-xy` in {pr}`3547`
|
||||
* Added note in docstring of `ManimColor` about class constructors by {user}`JasonGrace2282` in {pr}`3554`
|
||||
* Improve documentation section about contributing to docs by {user}`chopan050` in {pr}`3555`
|
||||
* Removed duplicated documentation for -s / --save_last_frame CLI flag by {user}`Gixtox` in {pr}`3528`
|
||||
* Updated Docker instructions to use bash from the PATH by {user}`NotWearingPants` in {pr}`3582`
|
||||
* Fixed typo in `value_tracker.py` by {user}`yuan-xy` in {pr}`3594`
|
||||
* Added `ref_class` for `BooleanOperations` in Example Gallery by {user}`JasonGrace2282` in {pr}`3598`
|
||||
* Changed `Vector3` to `Vector3D` in contributing docs by {user}`JasonGrace2282` in {pr}`3639`
|
||||
* Added some examples for `Mobject`/`VMobject` methods by {user}`JasonGrace2282` in {pr}`3641`
|
||||
* Fixed broken link to Poetry's installation guide in the documentation by {user}`biinnnggggg` in {pr}`3692`
|
||||
* Fixed minor grammatical errors found in the index page of the documentation by {user}`biinnnggggg` in {pr}`3690`
|
||||
* Fixed typo on page about translations by {user}`biinnnggggg` in {pr}`3696`
|
||||
* Fixed outdated description of CLI option in Manim's Output Settings by {user}`HairlessVillager` in {pr}`3674`
|
||||
* Mention pixi in installation guide by {user}`pavelzw` in {pr}`3678`
|
||||
* Updated typing guidelines by {user}`JasonGrace2282` in {pr}`3704`
|
||||
* Updated documentation and typings for `ParametricFunction` by {user}`danielzsh` in {pr}`3703`
|
||||
* Fixed docstring markup in `Rotate` by {user}`TheCrowned` in {pr}`3721`
|
||||
* Improve consistency in axis label example by {user}`amrear` in {pr}`3730`
|
||||
|
||||
### Maintenance and Testing
|
||||
|
||||
* Fixed wrong path in action building downloadable docs by {user}`behackl` in {pr}`3450`
|
||||
* Add type hints to `_config` by {user}`Viicos` in {pr}`3440`
|
||||
* Update dependency constraints, fix deprecation warnings by {user}`Viicos` in {pr}`3376`
|
||||
* Update Docker base image to python3.12-slim (#3458) by {user}`PikaBlue107` in {pr}`3459`
|
||||
* Fixed `line_join` to `joint_type` in example_scenes/basic.py by {user}`szchixy` in {pr}`3510`
|
||||
* Fixed :attr:`.Mobject.animate` type-hint to allow LSP autocomplete by {user}`JasonGrace2282` in {pr}`3543`
|
||||
* Finish TODO's in ``contributing/typings.rst`` by {user}`JasonGrace2282` in {pr}`3545`
|
||||
* Fixed use of `Mobject`'s deprecated `get_*()` and `set_*()` methods in Cairo tests by {user}`JasonGrace2282` in {pr}`3549`
|
||||
* Added support for Manim type aliases in Sphinx docs and added new TypeAliases by {user}`chopan050` in {pr}`3484`
|
||||
* Fixed typing of `Animation` by {user}`dandavison` in {pr}`3568`
|
||||
* Added some TODOs for future use of `ManimFrame` by {user}`chopan050` in {pr}`3553`
|
||||
* Fixed typehint of :attr:`InternalPoint2D_Array` by {user}`JasonGrace2282` in {pr}`3592`
|
||||
* Fixed error in Windows CI pipeline by {user}`behackl` in {pr}`3611`
|
||||
* Fixed type hint of indication.py by {user}`yuan-xy` in {pr}`3613`
|
||||
* Revert vector type aliases to NumPy ndarrays by {user}`chopan050` in {pr}`3595`
|
||||
* Run `poetry lock --no-update` by {user}`JasonGrace2282` in {pr}`3621`
|
||||
* Code Cleanup: removing unused imports and global variables by {user}`JasonGrace2282` in {pr}`3620`
|
||||
* Fixed type hint of `Vector` direction parameter by {user}`JasonGrace2282` in {pr}`3640`
|
||||
* Flake8 rule C901 is about McCabe code complexity by {user}`cclauss` in {pr}`3673`
|
||||
* Updated year in license by {user}`JasonGrace2282` in {pr}`3689`
|
||||
* Automated copyright updating for docs by {user}`JasonGrace2282` in {pr}`3708`
|
||||
* Fixed some typehints in `mobject.py` by {user}`JasonGrace2282` in {pr}`3668`
|
||||
* Search for type aliases if TYPE_CHECKING by {user}`JasonGrace2282` in {pr}`3671`
|
||||
* Follow-up to graph layout cleanup: improvements for tests and typing by {user}`behackl` in {pr}`3728`
|
||||
* GH Actions: Changed from macos-latest to macos-13 by {user}`JasonGrace2282` in {pr}`3729`
|
||||
* Fixed return type inconsistency for `get_anchors()` by {user}`JinchuLi2002` in {pr}`3214`
|
||||
* Prepared new release: `v0.18.1` by {user}`behackl` in {pr}`3719`
|
||||
|
||||
#### Dependency Version Changes
|
||||
|
||||
* Bump jupyter-server from 2.9.1 to 2.11.2 by {user}`dependabot` in {pr}`3497`
|
||||
* Bump github/codeql-action from 2 to 3 by {user}`dependabot` in {pr}`3567`
|
||||
* Bump actions/upload-artifact from 3 to 4 by {user}`dependabot` in {pr}`3566`
|
||||
* Bump actions/setup-python from 4 to 5 by {user}`dependabot` in {pr}`3565`
|
||||
* updated several packages (pillow, jupyterlab, notebook, jupyterlab-lsp, jinja2, gitpython) by {user}`behackl` in {pr}`3593`
|
||||
* Update jupyter.rst by {user}`abul4fia` in {pr}`3630`
|
||||
* Bump black from 23.12.1 to 24.3.0 by {user}`dependabot` in {pr}`3649`
|
||||
* Bump cryptography from 42.0.0 to 42.0.4 by {user}`dependabot` in {pr}`3629`
|
||||
* Bump actions/cache from 3 to 4 by {user}`dependabot` in {pr}`3607`
|
||||
* Bump FedericoCarboni/setup-ffmpeg from 2 to 3 by {user}`dependabot` in {pr}`3608`
|
||||
* Bump ssciwr/setup-mesa-dist-win from 1 to 2 by {user}`dependabot` in {pr}`3609`
|
||||
* Bump idna from 3.6 to 3.7 by {user}`dependabot` in {pr}`3693`
|
||||
* Bump pillow from 10.2.0 to 10.3.0 by {user}`dependabot` in {pr}`3672`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci` in {pr}`3332`
|
||||
* Updated sphinx deps by {user}`JasonGrace2282` in {pr}`3720`
|
||||
|
||||
|
||||
## New Contributors
|
||||
* {user}`Lawqup` made their first contribution in {pr}`3461`
|
||||
* {user}`jcep` made their first contribution in {pr}`3477`
|
||||
* {user}`szchixy` made their first contribution in {pr}`3498`
|
||||
* {user}`PikaBlue107` made their first contribution in {pr}`3459`
|
||||
* {user}`yuan-xy` made their first contribution in {pr}`3509`
|
||||
* {user}`MathItYT` made their first contribution in {pr}`3516`
|
||||
* {user}`doaamuham` made their first contribution in {pr}`3499`
|
||||
* {user}`RobinPH` made their first contribution in {pr}`3428`
|
||||
* {user}`pauluhlenbruck` made their first contribution in {pr}`3513`
|
||||
* {user}`yashm277` made their first contribution in {pr}`3436`
|
||||
* {user}`TheMathematicFanatic` made their first contribution in {pr}`3408`
|
||||
* {user}`vvolhejn` made their first contribution in {pr}`3561`
|
||||
* {user}`hydromelvictor` made their first contribution in {pr}`3574`
|
||||
* {user}`dandavison` made their first contribution in {pr}`3568`
|
||||
* {user}`Gixtox` made their first contribution in {pr}`3528`
|
||||
* {user}`staghado` made their first contribution in {pr}`3429`
|
||||
* {user}`biinnnggggg` made their first contribution in {pr}`3692`
|
||||
* {user}`HairlessVillager` made their first contribution in {pr}`3674`
|
||||
* {user}`SirJamesClarkMaxwell` made their first contribution in {pr}`3675`
|
||||
* {user}`danielzsh` made their first contribution in {pr}`3703`
|
||||
* {user}`TheCrowned` made their first contribution in {pr}`3721`
|
||||
* {user}`MontroyJosh` made their first contribution in {pr}`3718`
|
||||
* {user}`amrear` made their first contribution in {pr}`3730`
|
||||
|
||||
**Full Changelog**: https://github.com/ManimCommunity/manim/compare/v0.18.0.post0...v0.18.1
|
||||
587
docs/source/changelog/0.19.0-changelog.rst
Normal file
587
docs/source/changelog/0.19.0-changelog.rst
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
*******
|
||||
v0.19.0
|
||||
*******
|
||||
|
||||
:Date: January 20, 2025
|
||||
|
||||
Major Changes
|
||||
=============
|
||||
|
||||
With the release of Manim v0.19.0, we've made lots of progress with making
|
||||
Manim easier to install!
|
||||
|
||||
One of the biggest changes in this release is the replacement of the external
|
||||
``ffmpeg`` dependency with the ``pyav`` library. This means that users no longer
|
||||
have to install ``ffmpeg`` in order to use Manim - they can just ``pip install manim``
|
||||
and it will work!
|
||||
|
||||
In light of this change, we also rewrote our :ref:`installation docs <local-installation>`
|
||||
to recommend using a new tool called `uv <https://docs.astral.sh/uv/>`_ to install Manim.
|
||||
|
||||
.. note::
|
||||
|
||||
Do not worry if you installed Manim with any previous methods, like homebrew, pip,
|
||||
choco, or scoop. Those methods will still work, and are not deprecated. However,
|
||||
the recommended way to install Manim is now with `uv <https://docs.astral.sh/uv/>`_.
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
A total of 54 people contributed to this
|
||||
release. People with a '+' by their names authored a patch for the first
|
||||
time.
|
||||
|
||||
* Aarush Deshpande
|
||||
* Abulafia
|
||||
* Achille Fouilleul +
|
||||
* Benjamin Hackl
|
||||
* CJ Lee +
|
||||
* Cameron Burdgick +
|
||||
* Chin Zhe Ning
|
||||
* Christopher Hampson +
|
||||
* ChungLeeCN +
|
||||
* Eddie Ruiz +
|
||||
* F. Muenkel +
|
||||
* Francisco Manríquez Novoa
|
||||
* Geoo Chi +
|
||||
* Henrik Skov Midtiby +
|
||||
* Hugo Chargois +
|
||||
* Irvanal Haq +
|
||||
* Jay Gupta +
|
||||
* Laifsyn +
|
||||
* Larry Skuse +
|
||||
* Nemo2510 +
|
||||
* Nikhil Iyer
|
||||
* Nikhila Gurusinghe +
|
||||
* Rehmatpal Singh +
|
||||
* Romit Mohane +
|
||||
* Saveliy Yusufov +
|
||||
* Sir James Clark Maxwell
|
||||
* Sophia Wisdom +
|
||||
* Tristan Schulz
|
||||
* VPC +
|
||||
* Victorien
|
||||
* Xiuyuan (Jack) Yuan +
|
||||
* alembcke
|
||||
* anagorko +
|
||||
* czuzu +
|
||||
* fogsong233 +
|
||||
* jkjkil4 +
|
||||
* modjfy +
|
||||
* nitzanbueno +
|
||||
* yang-tsao +
|
||||
|
||||
|
||||
The patches included in this release have been reviewed by
|
||||
the following contributors.
|
||||
|
||||
* Aarush Deshpande
|
||||
* Achille Fouilleul
|
||||
* Benjamin Hackl
|
||||
* Christopher Hampson
|
||||
* Eddie Ruiz
|
||||
* Francisco Manríquez Novoa
|
||||
* Henrik Skov Midtiby
|
||||
* Hugo Chargois
|
||||
* Irvanal Haq
|
||||
* Jay Gupta
|
||||
* Jérome Eertmans
|
||||
* Nemo2510
|
||||
* Nikhila Gurusinghe
|
||||
* OliverStrait
|
||||
* Saveliy Yusufov
|
||||
* Sir James Clark Maxwell
|
||||
* Tristan Schulz
|
||||
* VPC
|
||||
* Victorien
|
||||
* Xiuyuan (Jack) Yuan
|
||||
* alembcke
|
||||
* github-advanced-security[bot]
|
||||
|
||||
Pull requests merged
|
||||
====================
|
||||
|
||||
A total of 138 pull requests were merged for this release.
|
||||
|
||||
Highlights
|
||||
----------
|
||||
|
||||
* :pr:`3501`: Replaced external ``ffmpeg`` dependency with ``pyav``
|
||||
This change removes the need to have ``ffmpeg`` available as a command line tool
|
||||
when using Manim. While ``pyav`` technically also uses ``ffmpeg`` internally,
|
||||
the maintainers of ``pyav`` distribute it in their binary wheels.
|
||||
|
||||
|
||||
* :pr:`3518`: Created a :class:`.HSV` color class, and added support for custom color spaces
|
||||
This extends the color system of Manim and adds support to implement custom color spaces.
|
||||
See the implementation of :class:`.HSV` for a practical example.
|
||||
|
||||
|
||||
* :pr:`3930`: Completely reworked the installation instructions
|
||||
As a consequence of removing the need for the external ``ffmpeg`` dependency,
|
||||
we have reworked and massively simplified the installation instructions. Given
|
||||
that practically, user-written scenes are effectively small self-contained Python
|
||||
projects, the new instructions strongly recommend using the
|
||||
`project and dependency management tool uv <https://docs.astral.sh/uv/>`__ to ensure
|
||||
a consistent and reproducible environment.
|
||||
|
||||
|
||||
* :pr:`3967`: Added support for Python 3.13
|
||||
This adds support for Python 3.13, which brings the range of currently supported
|
||||
Python versions to 3.9 -- 3.13.
|
||||
|
||||
|
||||
* :pr:`3966`: :class:`.VGroup` can now be initialized with :class:`.VMobject` iterables
|
||||
Groups of Mobjects can now be created by passing an iterable to the :class:`.VGroup`
|
||||
constructors::
|
||||
|
||||
my_group = VGroup(Dot() for _ in range(10))
|
||||
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* :pr:`3797`: Replaced ``Code.styles_list`` with :meth:`.Code.get_styles_list`
|
||||
The ``styles_list`` attribute of the :class:`.Code` class has been replaced with
|
||||
a class method :meth:`.Code.get_styles_list`. This method returns a list of all
|
||||
available values for the ``formatter_style`` argument of :class:`.Code`.
|
||||
|
||||
|
||||
* :pr:`3884`: Renamed parameters and variables conflicting with builtin functions
|
||||
To avoid having keyword arguments named after builtin functions, the following
|
||||
two changes were made to user-facing functions:
|
||||
|
||||
- ``ManimColor.from_hex(hex=...)`` is now ``ManimColor.from_hex(hex_str=...)``
|
||||
- ``Scene.next_section(type=...)`` is now ``Scene.next_section(section_type=...)``
|
||||
|
||||
|
||||
* :pr:`3922`: Removed ``inner_radius`` and ``outer_radius`` from :class:`.Sector` constructor
|
||||
To construct a :class:`.Sector`, you now need to specify a ``radius`` (and an ``angle``).
|
||||
In particular, :class:`.AnnularSector` still accepts both ``inner_radius`` and ``outer_radius``
|
||||
arguments.
|
||||
|
||||
|
||||
* :pr:`3964`: Allow :class:`.SurroundingRectangle` to accept multiple Mobjects
|
||||
This changes the signature of :class:`.SurroundingRectangle` to accept
|
||||
a sequence of Mobjects instead of a single Mobject. As a consequence, other
|
||||
arguments that could be specified as positional ones before now need to be
|
||||
specified as keyword arguments::
|
||||
|
||||
SurroundingRectangle(some_mobject, RED, 0.3) # raises error now
|
||||
SurroundingRectangle(some_mobject, color=RED, buff=0.3) # correct usage
|
||||
|
||||
|
||||
* :pr:`4115`: Completely rewrite the implementation of the :class:`.Code` mobject
|
||||
This includes several breaking changes to the interface of the class to make it
|
||||
more consistent. See the documentation of :class:`.Code` for a detailed description
|
||||
of the new interface, and the description of the pull request :pr:`4115` for
|
||||
an overview of changes to the old keyword arguments.
|
||||
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
* :pr:`3148`: Added a ``colorscale`` argument to :meth:`.CoordinateSystem.plot`
|
||||
|
||||
|
||||
* :pr:`3612`: Add three animations that together simulate a typing animation
|
||||
|
||||
|
||||
* :pr:`3754`: Add ``@`` shorthand for :meth:`.Axes.coords_to_point` and :meth:`.Axes.point_to_coords`
|
||||
|
||||
|
||||
* :pr:`3876`: Add :meth:`.Animation.set_default` class method
|
||||
|
||||
|
||||
* :pr:`3903`: Preserve colors of LaTeX coloring commands
|
||||
|
||||
|
||||
* :pr:`3913`: Added :mod:`.DVIPSNAMES` and :mod:`.SVGNAMES` color palettes
|
||||
|
||||
|
||||
* :pr:`3933`: Added :class:`.ConvexHull`, :class:`.ConvexHull3D`, :class:`.Label` and :class:`.LabeledPolygram`
|
||||
|
||||
|
||||
* :pr:`3992`: Add darker, lighter and contrasting methods to :class:`.ManimColor`
|
||||
|
||||
|
||||
* :pr:`3997`: Add a time property to scene (:attr:`.Scene.time`)
|
||||
|
||||
|
||||
* :pr:`4039`: Added the ``delay`` parameter to :func:`.turn_animation_into_updater`
|
||||
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* :pr:`3829`: Rewrite :func:`~.bezier.get_quadratic_approximation_of_cubic` to produce smoother animated curves
|
||||
|
||||
|
||||
* :pr:`3855`: Log execution time of sample scene in the ``manim checkhealth`` command
|
||||
|
||||
|
||||
* :pr:`3888`: Significantly reduce rendering time with a separate thread for writing frames to stream
|
||||
|
||||
|
||||
* :pr:`3890`: Better error messages for :class:`.DrawBorderThenFill`
|
||||
|
||||
|
||||
* :pr:`3893`: Improve line rendering performance of :class:`.Cylinder`
|
||||
|
||||
|
||||
* :pr:`3901`: Changed :attr:`.Square.side_length` attribute to a property
|
||||
|
||||
|
||||
* :pr:`3965`: Added the ``scale_stroke`` boolean parameter to :meth:`.VMobject.scale`
|
||||
|
||||
|
||||
* :pr:`3974`: Made videos embedded in Google Colab by default
|
||||
|
||||
|
||||
* :pr:`3982`: Refactored ``run_time`` validation for :class:`.Animation` and :meth:`.Scene.wait`
|
||||
|
||||
|
||||
* :pr:`4017`: Allow animations with ``run_time=0`` and implement convenience :class:`.Add` animation
|
||||
|
||||
|
||||
* :pr:`4034`: Draw more accurate circular :class:`.Arc` mobjects for large angles
|
||||
|
||||
|
||||
* :pr:`4051`: Add ``__hash__`` method to :class:`.ManimColor`
|
||||
|
||||
|
||||
* :pr:`4108`: Remove duplicate declaration of ``__all__`` in :mod:`.vectorized_mobject`
|
||||
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
|
||||
* :pr:`3760`: Optimize :meth:`.VMobject.pointwise_become_partial`
|
||||
|
||||
|
||||
* :pr:`3765`: Optimize :class:`.VMobject` methods which append to ``points``
|
||||
|
||||
|
||||
* :pr:`3766`: Created and optimized Bézier splitting functions such as :func:`~.utils.bezier.partial_bezier_points()` in :mod:`manim.utils.bezier`
|
||||
|
||||
|
||||
* :pr:`3767`: Optimized :func:`manim.utils.bezier.get_smooth_cubic_bezier_handle_points()`
|
||||
|
||||
|
||||
* :pr:`3768`: Optimized :func:`manim.utils.bezier.is_closed`
|
||||
|
||||
|
||||
* :pr:`3960`: Optimized :func:`~.bezier.interpolate` and :func:`~.bezier.bezier` in :mod:`manim.utils.bezier`
|
||||
|
||||
|
||||
|
||||
Fixed bugs
|
||||
----------
|
||||
|
||||
* :pr:`3706`: Fixed :meth:`.Line.put_start_and_end_on` to use the actual end of an :class:`.Arrow3D`
|
||||
|
||||
|
||||
* :pr:`3732`: Fixed infinite loop in OpenGL :meth:`.BackgroundRectangle.get_color`
|
||||
|
||||
|
||||
* :pr:`3756`: Fix assertions and improve error messages when adding submobjects
|
||||
|
||||
|
||||
* :pr:`3778`: Fixed :func:`.there_and_back_with_pause` rate function behaviour with different ``pause_ratio`` values
|
||||
|
||||
|
||||
* :pr:`3786`: Fix :class:`.DiGraph` edges not fading correctly on :class:`.FadeIn` and :class:`.FadeOut`
|
||||
|
||||
|
||||
* :pr:`3790`: Fixed the :func:`.get_nth_subpath` function expecting a numpy array
|
||||
|
||||
|
||||
* :pr:`3832`: Convert audio files to ``.wav`` before passing to pydub
|
||||
|
||||
|
||||
* :pr:`3680`: Fixed behavior of ``config.background_opacity < 1``
|
||||
|
||||
|
||||
* :pr:`3839`: Fixed :attr:`.ManimConfig.format` not updating movie file extension
|
||||
|
||||
|
||||
* :pr:`3885`: Fixed :meth:`.OpenGLMobject.invert` not reassembling family
|
||||
|
||||
|
||||
* :pr:`3951`: Call :meth:`.Animation.finish` for animations in an :class:`.AnimationGroup`
|
||||
|
||||
|
||||
* :pr:`4013`: Fixed scene skipping for :attr:`ManimConfig.upto_animation_number` set to 0
|
||||
|
||||
|
||||
* :pr:`4089`: Fixed bug with opacity of :class:`.ImageMobject`
|
||||
|
||||
|
||||
* :pr:`4091`: Fixed :meth:`.VMobject.add_points_as_corners` to safely handle empty ``points`` parameter
|
||||
|
||||
|
||||
Documentation-related changes
|
||||
-----------------------------
|
||||
|
||||
* :pr:`3669`: Added a :mod:`manim.typing` guide
|
||||
|
||||
|
||||
* :pr:`3715`: Added docstrings to Brace
|
||||
|
||||
|
||||
* :pr:`3745`: Underline tag should be ``<u></u>`` in the documentation
|
||||
|
||||
|
||||
* :pr:`3818`: Automatically document usages of :class:`typing.TypeVar`
|
||||
|
||||
|
||||
* :pr:`3849`: Fix incorrect ``versionadded`` version number in plugin section in docs
|
||||
|
||||
|
||||
* :pr:`3851`: Rename ``manim.typing.Image`` type aliases to :class:`.PixelArray` to avoid conflict with ``PIL.Image``
|
||||
|
||||
|
||||
* :pr:`3857`: Update installation instructions for MacOS (via dedicated brew formula)
|
||||
|
||||
|
||||
* :pr:`3878`: Fixed typehint in ``types.rst`` and replaced outdated reference to ``manim.typing.Image`` with :class:`manim.typing.PixelArray`
|
||||
|
||||
|
||||
* :pr:`3924`: Fix ``SyntaxWarning`` when building docs + use Python 3.13 for readthedocs build
|
||||
|
||||
|
||||
* :pr:`3958`: Fix: ``.to_edge``'s example demonstration in docs
|
||||
|
||||
|
||||
* :pr:`3972`: Refining documentations for :mod:`.moving_camera_scene` module
|
||||
|
||||
|
||||
* :pr:`4032`: Bump version and create changelog for ``v0.19.0``
|
||||
|
||||
|
||||
* :pr:`4044`: Added support for autodocumenting type aliases that use the ``type`` syntax
|
||||
|
||||
|
||||
* :pr:`4065`: Polish documentation of :mod:`.utils.color.core` and remove ``interpolate_array`` function
|
||||
|
||||
|
||||
* :pr:`4077`: Update README and documentation landing page, improve way how 3b1b is credited
|
||||
|
||||
|
||||
* :pr:`4100`: Add wavy square example to :class:`.Homotopy`
|
||||
|
||||
|
||||
* :pr:`4107`: Corrected a typo in the deep dive guide
|
||||
|
||||
|
||||
* :pr:`4116`: Fix broken link to Poetry installation in contribution docs
|
||||
|
||||
|
||||
Type Hints
|
||||
----------
|
||||
|
||||
* :pr:`3751`: Added typehints to :mod:`manim.utils.iterables`
|
||||
|
||||
|
||||
* :pr:`3803`: Added typings to :class:`.OpenGLMobject`
|
||||
|
||||
|
||||
* :pr:`3902`: fixed a wrong type hint in :meth:`.Scene.restructure_mobjects`
|
||||
|
||||
|
||||
* :pr:`3916`: fixed type hint in :meth:`.DrawBorderThenFill.interpolate_submobject`
|
||||
|
||||
|
||||
* :pr:`3926`: Fixed some typehints of :class:`.ParametricFunction`
|
||||
|
||||
|
||||
* :pr:`3940`: Fixed ``np.float_`` to ``np.float64`` while using numpy versions above 2.0
|
||||
|
||||
|
||||
* :pr:`3961`: Added typehints to :mod:`manim.mobject.geometry`
|
||||
|
||||
|
||||
* :pr:`3980`: Added new :class:`.PointND` and :class:`.PointND_Array` type aliases
|
||||
|
||||
|
||||
* :pr:`3988`: Added type hints to :mod:`manim.cli` module
|
||||
|
||||
|
||||
* :pr:`3999`: Add type annotations to :mod:`manim.utils`
|
||||
|
||||
|
||||
* :pr:`4006`: Stopped ignoring :mod:`manim.plugins` errors in ``mypy.ini``
|
||||
|
||||
|
||||
* :pr:`4007`: Added typings to :mod:`manim.__main__`
|
||||
|
||||
|
||||
* :pr:`4027`: Rename ``InternalPoint3D`` to :class:`~.typing.Point3D`, ``Point3D`` to :class:`~.Point3DLike` and other point-related type aliases
|
||||
|
||||
|
||||
* :pr:`4038`: Fixed type hint of :meth:`.Scene.play` to allow :attr:`.Mobject.animate`
|
||||
|
||||
|
||||
Internal Improvements and Automation
|
||||
------------------------------------
|
||||
|
||||
* :pr:`3737`: Fixed action for building downloadable documentation
|
||||
|
||||
|
||||
* :pr:`3761`: Use ``--py39-plus`` in pre-commit
|
||||
|
||||
|
||||
* :pr:`3777`: Add pyproject for ruff formatting
|
||||
|
||||
|
||||
* :pr:`3779`: Switch pre-commit to use ``ruff`` for linting
|
||||
|
||||
|
||||
* :pr:`3795`: Replace Pyupgrade with Ruff rule
|
||||
|
||||
|
||||
* :pr:`3812`: Fix MacOS LaTeX CI
|
||||
|
||||
|
||||
* :pr:`3853`: Change from tempconfig to a config fixture in tests
|
||||
|
||||
|
||||
* :pr:`3858`: Update docker to use ENV x=y instead of ENV x y
|
||||
|
||||
|
||||
* :pr:`3872`: Use ruff for pytest style
|
||||
|
||||
|
||||
* :pr:`3873`: Use ruff instead of flake8-simplify
|
||||
|
||||
|
||||
* :pr:`3877`: Fix pre-commit linting
|
||||
|
||||
|
||||
* :pr:`3780`: Add Ruff Lint
|
||||
|
||||
|
||||
* :pr:`3781`: Ignore Ruff format in git blame
|
||||
|
||||
|
||||
* :pr:`3881`: Standardize docstrings with ruff pydocstyle rules
|
||||
|
||||
|
||||
* :pr:`3882`: Change flake8-comprehensions and flake8-bugbear to ruff
|
||||
|
||||
|
||||
* :pr:`3887`: Fix typo from HSV PR
|
||||
|
||||
|
||||
* :pr:`3923`: Use Ruff pygrep rules
|
||||
|
||||
|
||||
* :pr:`3925`: Use Github Markdown on README
|
||||
|
||||
|
||||
* :pr:`3955`: Use ``subprocess`` instead of ``os.system``.
|
||||
|
||||
|
||||
* :pr:`3956`: Set AAC codec for audio in mp4 files, add transcoding utility
|
||||
|
||||
|
||||
* :pr:`4069`: Include Noto fonts in Docker image
|
||||
|
||||
|
||||
* :pr:`4102`: Remove PT004 from Ruff ignore rules
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* :pr:`3739`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3746`: Bump tqdm from 4.66.1 to 4.66.3
|
||||
|
||||
|
||||
* :pr:`3750`: Bump jinja2 from 3.1.3 to 3.1.4
|
||||
|
||||
|
||||
* :pr:`3776`: Bump requests from 2.31.0 to 2.32.0
|
||||
|
||||
|
||||
* :pr:`3784`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3794`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3796`: Bump tornado from 6.4 to 6.4.1
|
||||
|
||||
|
||||
* :pr:`3801`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3809`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3810`: Bump urllib3 from 2.2.1 to 2.2.2
|
||||
|
||||
|
||||
* :pr:`3823`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3827`: Fix docker build
|
||||
|
||||
|
||||
* :pr:`3834`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3835`: Bump docker/build-push-action from 5 to 6
|
||||
|
||||
|
||||
* :pr:`3841`: Bump certifi from 2024.2.2 to 2024.7.4
|
||||
|
||||
|
||||
* :pr:`3844`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3847`: Bump zipp from 3.18.2 to 3.19.1
|
||||
|
||||
|
||||
* :pr:`3865`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3880`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3889`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3895`: Lock `poetry.lock`
|
||||
|
||||
|
||||
* :pr:`3896`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3904`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3911`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3918`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3929`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`3931`: Bump cryptography from 43.0.0 to 43.0.1
|
||||
|
||||
|
||||
* :pr:`3987`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`4023`: Bump tornado from 6.4.1 to 6.4.2
|
||||
|
||||
|
||||
* :pr:`4035`: [pre-commit.ci] pre-commit autoupdate
|
||||
|
||||
|
||||
* :pr:`4037`: Cap ``pyav`` version
|
||||
197
docs/source/changelog/0.19.1-changelog.md
Normal file
197
docs/source/changelog/0.19.1-changelog.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
---
|
||||
short-title: v0.19.1
|
||||
description: Changelog for Manim v0.19.1
|
||||
---
|
||||
|
||||
# v0.19.1
|
||||
|
||||
Date
|
||||
: December 01, 2025
|
||||
|
||||
## What's Changed
|
||||
|
||||
### New Features
|
||||
|
||||
* Introduce seed in `random_color` method to produce colors deterministically by {user}`ishu9bansal` in {pr}`4265`
|
||||
* Add support for arithmetic operators `//`, `%`, `*`, `**` and `/` on `ValueTracker` by {user}`fmuenkel` in {pr}`4351`
|
||||
* Add `TangentialArc` mobject by {user}`Brainsucker92` in {pr}`4469`
|
||||
|
||||
|
||||
### Fixed Bugs and Enhancements
|
||||
|
||||
* Fix environment formatting for Tex() mobject by {user}`fmuenkel` in {pr}`4159`
|
||||
* Improved consistency of rate_function implementations by {user}`BenKirkels` in {pr}`4144`
|
||||
* Make new `Code` mobject compatible with OpenGL renderer by {user}`behackl` in {pr}`4164`
|
||||
* Fix HSL color ordering in ManimColor by {user}`thehugwizard` in {pr}`4202`
|
||||
* Fix return type of `Polygram.get_vertex_groups()` and rename variables in `.round_corners()` by {user}`chopan050` in {pr}`4063`
|
||||
* Improve `Mobject.align_data` docstring by {user}`irvanalhaq9` in {pr}`4152`
|
||||
* Fix :meth:`VMobject.pointwise_become_partial` failing when `vmobject` is `self` by {user}`irvanalhaq9` in {pr}`4193`
|
||||
* Fix `add_points_as_corners` not connecting single point to existing path by {user}`irvanalhaq9` in {pr}`4219`
|
||||
* Complete typing for logger_utils.py by {user}`fmuenkel` in {pr}`4134`
|
||||
* Fix(graph): Allow any Line subclass as edge_type in Graph/DiGraph by {user}`Akshat-Mishra-py` in {pr}`4251`
|
||||
* Replace exceptions, remove unused parameters, and fix type hints in `Animation`, `ShowPartial`, `Create`, `ShowPassingFlash`, and `DrawBorderThenFill` by {user}`irvanalhaq9` in {pr}`4214`
|
||||
* Fix: `Axes` submobject colors are not being set properly by {user}`ishu9bansal` in {pr}`4291`
|
||||
* Refactor `Rotating` and add docstrings to `Mobject.rotate()` and `Rotating` by {user}`irvanalhaq9` in {pr}`4147`
|
||||
* Fix default config of `manim init project` to use correct `pixel_height` and `pixel_width` by {user}`StevenH34` in {pr}`4213`
|
||||
* Handle opacity and transparent images by {user}`henrikmidtiby` in {pr}`4313`
|
||||
* Gracefully fall back when version metadata is missing by {user}`mohiuddin-khan-shiam` in {pr}`4324`
|
||||
* Fix for issue 4255 - Do not clear points when the number of curves is zero by {user}`henrikmidtiby` in {pr}`4320`
|
||||
* Use utf-8 encoding to read generated .tex files. by {user}`OliverStrait` in {pr}`4334`
|
||||
* Add zero to vmobject points to remove negative zeros in `get_mobject_key` by {user}`elshorbagyx` in {pr}`4332`
|
||||
* Ensure `stroke_width` attribute of `SVGMobject` is not set to `None` by {user}`henrikmidtiby` in {pr}`4319`
|
||||
* Fix `Prism` incorrectly rendering with `dimensions=[2, 2, 2]` in OpenGL by {user}`ra1u` in {pr}`4003`
|
||||
* Fix `BraceLabel.change_label()` and document `BraceText` by {user}`henrikmidtiby` in {pr}`4347`
|
||||
* Include `Text.gradient` in hash to properly regenerate `Text` when its gradient changes by {user}`AbhilashaTandon` in {pr}`4099`
|
||||
* Fixed surface animations in OpenGL by {user}`nubDotDev` in {pr}`4286`
|
||||
* Add type hints and support for arithmetic operators `+` and `-` on `ValueTracker` by {user}`fmuenkel` in {pr}`4129`
|
||||
* Fix duplicate references in `Scene.mobjects` after `ReplacementTransform` with existing target mobject by {user}`irvanalhaq9` in {pr}`4242`
|
||||
* Optimize `always_redraw()` by reducing `Mobject` copying in `Mobject.become()` by {user}`chopan050` in {pr}`4357`
|
||||
* Enhance `manim cfg show` output and add info-level logging for config files read by {user}`xnov18` in {pr}`4375`
|
||||
* Let `Cube` use Bevel type line joints by {user}`nubDotDev` in {pr}`4361`
|
||||
* Properly define `init_points` methods for use in OpenGL instead of defining `init_points = generate_points` by {user}`chopan050` in {pr}`4360`
|
||||
* Allow passing a tuple to `buff` in `SurroundingRectangle` to specify buffer in x and y direction independently by {user}`nubDotDev` in {pr}`4390`
|
||||
* Rewrite `color_gradient` to always return a list of ManimColors by {user}`henrikmidtiby` in {pr}`4380`
|
||||
* Ensure leading whitespace does not change line height for lines in CodeMobject by {user}`behackl` in {pr}`4392`
|
||||
* Simplify the function `remove_invisible_chars` in `text_mobject.py` by {user}`henrikmidtiby` in {pr}`4394`
|
||||
* Fix some config options specified via `--config_file` not being respected properly by {user}`behackl` in {pr}`4401`
|
||||
* Fix: Correct resolution tuple order to (height, width) by {user}`Nikhil172913832` in {pr}`4440`
|
||||
* Ensure that start and end points are stored as float values in Line3D by {user}`SirJamesClarkMaxwell` in {pr}`4080`
|
||||
* OpenGL: Fix iterated nesting in `DecimalNumber.set_value` by {user}`henrikmidtiby` in {pr}`4373`
|
||||
* Update default resolution in CLI to match Manim’s 1920x1080 default settings by {user}`SASHAKT1290` in {pr}`4452`
|
||||
* Better parsing of color styles in CodeMobject by {user}`SirJamesClarkMaxwell` in {pr}`4454`
|
||||
* Allow selection of all scenes to render using '*' by {user}`NightyStudios` in {pr}`4470`
|
||||
* Prevent mutation of `about_point` in `apply_points_function_about_point` by {user}`Morkunas` in {pr}`4478`
|
||||
* Fix behavior of `Mobject.suspend_updating`: when only suspending parent mobject, let children continue updating by {user}`behackl` in {pr}`4402`
|
||||
* Allow passing a `buff` to `LabeledDot` by {user}`nubDotDev` in {pr}`4403`
|
||||
* Pass ndarrays to `mapbox_earcut.triangulate_float32()` to fix `TypeError` in `mapbox_earcut==2.0.0` by {user}`GuiCT` in {pr}`4479`
|
||||
* Fix duplicated arrow tips in DashedVMobject (issue #3220) by {user}`jakekinchen` in {pr}`4484`
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* Add docstring to :meth:`.Mobject.get_family` by {user}`irvanalhaq9` in {pr}`4127`
|
||||
* Fix link formatting and clarify the distinction between Manim versions in index.rst by {user}`irvanalhaq9` in {pr}`4131`
|
||||
* Add instructions for installing system utilities `cairo` and `pkg-config` via Homebrew on MacOS by {user}`behackl` in {pr}`4146`
|
||||
* Add missing line break in Code of Conduct's conflict of interest policy by {user}`Hasan-Mesbaul-Ali-Taher` in {pr}`4185`
|
||||
* Fix links to Pango website by {user}`ragibson` in {pr}`4217`
|
||||
* Replace poetry with uv in the README by {user}`xinoehp512` in {pr}`4226`
|
||||
* Improve docstring for `interpolate` method in `Mobject` class by {user}`irvanalhaq9` in {pr}`4149`
|
||||
* Add docstrings to `Line` and remove `None` handling for `path_arc` parameter by {user}`irvanalhaq9` in {pr}`4223`
|
||||
* Add docstring to :meth:`Mobject.family_members_with_points` by {user}`irvanalhaq9` in {pr}`4128`
|
||||
* Update incorrect docstring for :attr:`ManimConfig.gui_location` property by {user}`SAYAN02-DEV` in {pr}`4254`
|
||||
* Fix formatting of color space documentation by {user}`behackl` in {pr}`4274`
|
||||
* Enhance and Paraphrase Description of ManimCE in README.md by {user}`irvanalhaq9` in {pr}`4141`
|
||||
* docs: add explanation about the rate_func in the custom animation by {user}`pedropxoto` in {pr}`4278`
|
||||
* Fixed artifact in docstring of Animation by {user}`barollet` in {pr}`4283`
|
||||
* Rename update function `dot_position` to `update_label` in `.add_updater` example by {user}`irvanalhaq9` in {pr}`4196`
|
||||
* Fix Microsoft typo in `TexFontTemplateLibrary` scene in `example_scenes/advanced_tex_fonts.py` by {user}`alterdim` in {pr}`4305`
|
||||
* Improved readability, grammar, as well as added docstrings for consistency by {user}`NASAnerd05` in {pr}`4267`
|
||||
* Add docstrings for `ChangingDecimal` and `ChangeDecimalToValue` by {user}`haveheartt` in {pr}`4346`
|
||||
* Fix Sphinx exceptions when trying to build documentation via latex / as pdf by {user}`behackl` in {pr}`4370`
|
||||
* Added license information to documentation landing page by {user}`Nikil-D-Gr8` in {pr}`3986`
|
||||
* Set the default Python version to 3.13 in the uv installation guide by {user}`henrikmidtiby` in {pr}`4480`
|
||||
|
||||
|
||||
### Maintenance and Testing
|
||||
|
||||
* Change project management tool from poetry to uv by {user}`behackl` in {pr}`4138`
|
||||
* Re-add ffmpeg as dependency within Docker image by {user}`behackl` in {pr}`4150`
|
||||
* Add tests for Matrix, DecimalMatrix, IntegerMatrix by {user}`pdrzan` in {pr}`4279`
|
||||
* Add tests for polylabel utility by {user}`giolucasd` in {pr}`4269`
|
||||
* Add support for `pycodestyle W` rule in Ruff by {user}`KaiqueDultra` in {pr}`4276`
|
||||
* Fix files with few MyPy typing errors by {user}`henrikmidtiby` in {pr}`4263`
|
||||
* Explicitly mention all files that mypy should ignore in the `mypy.ini` configuration file by {user}`henrikmidtiby` in {pr}`4306`
|
||||
* Remove dead code from `scene.py` and `vector_space_scene.py` by {user}`henrikmidtiby` in {pr}`4310`
|
||||
* Add type annotations to `scene.py` and `vector_space_scene.py` by {user}`henrikmidtiby` in {pr}`4260`
|
||||
* Replace setup-texlive-action in CI workflow by {user}`behackl` in {pr}`4326`
|
||||
* Adding type annotations to polyhedra.py and matrix.py by {user}`henrikmidtiby` in {pr}`4322`
|
||||
* Handling typing errors in text/numbers.py by {user}`henrikmidtiby` in {pr}`4317`
|
||||
* Move `configure_pygui` into a `Scene` method and remove `manim.gui` by {user}`chopan050` in {pr}`4314`
|
||||
* Add typing annotations to svg_mobject.py by {user}`henrikmidtiby` in {pr}`4318`
|
||||
* Add type annotations to `mobject/svg/brace.py` and default to `label_constructor=Text` in `BraceText` by {user}`henrikmidtiby` in {pr}`4309`
|
||||
* Add classes `MethodWithArgs`, `SceneInteractContinue` and `SceneInteractRerun` inside new module `manim.data_structures` by {user}`chopan050` in {pr}`4315`
|
||||
* Fix typo in import of OpenGLCamera in `utils/hashing.py` by {user}`fmuenkel` in {pr}`4352`
|
||||
* Add type annotations to `manim/renderer/shader.py` by {user}`henrikmidtiby` in {pr}`4350`
|
||||
* Add type annotations to `tex_mobject.py` by {user}`henrikmidtiby` in {pr}`4355`
|
||||
* Add type annotations to `three_d_camera.py` by {user}`henrikmidtiby` in {pr}`4356`
|
||||
* Revert change of default value for tex_environment by {user}`henrikmidtiby` in {pr}`4358`
|
||||
* Add type hints to `scene_file_writer.py`, `section.py`, and `zoomed_scene.py` by {user}`fmuenkel` in {pr}`4133`
|
||||
* Add type annotations for most of `camera` and `mobject.graphing` by {user}`henrikmidtiby` in {pr}`4125`
|
||||
* Add `VectorNDLike` type aliases by {user}`chopan050` in {pr}`4068`
|
||||
* Add type annotations to `dot_cloud.py`, `vectorized_mobject_rendering.py` and `opengl_three_dimensions.py` by {user}`henrikmidtiby` in {pr}`4359`
|
||||
* Add type annotations to `indication.py` by {user}`henrikmidtiby` in {pr}`4367`
|
||||
* Add type annotations to `composition.py` by {user}`henrikmidtiby` in {pr}`4366`
|
||||
* Add type annotations to `growing.py` by {user}`henrikmidtiby` in {pr}`4368`
|
||||
* Add type annotations to `movement.py` by {user}`henrikmidtiby` in {pr}`4371`
|
||||
* Exclude check for cyclic imports by CodeQL by {user}`behackl` in {pr}`4384`
|
||||
* Refactor imports from `collections.abc`, `typing` and `typing_extensions` for Python 3.9 by {user}`chopan050` in {pr}`4353`
|
||||
* Add type annotations to `opengl_renderer_window.py` by {user}`fmuenkel` in {pr}`4363`
|
||||
* Rename `SceneFileWriter.save_final_image()` to `save_image()` by {user}`fmuenkel` in {pr}`4378`
|
||||
* Add type annotations to `text_mobject.py` by {user}`henrikmidtiby` in {pr}`4381`
|
||||
* Rename types like `RGBA_Array_Float` to `FloatRGBA` and add types like `FloatRGBA_Array` by {user}`chopan050` in {pr}`4386`
|
||||
* Add type annotations to `opengl_geometry.py` by {user}`henrikmidtiby` in {pr}`4396`
|
||||
* Add type annotations to `moving_camera.py` by {user}`henrikmidtiby` in {pr}`4397`
|
||||
* Add type annotations to `opengl_mobject.py` by {user}`RBerga06` in {pr}`4398`
|
||||
* Fix failing pre-commit tests by {user}`cclauss` in {pr}`4434`
|
||||
* Add type annotations to `cairo_renderer.py` by {user}`fmuenkel` in {pr}`4393`
|
||||
* Fix type errors and add typings for `Mobject.apply_function()`, its derivatives, and other utility functions by {user}`godalming123` in {pr}`4228`
|
||||
* Bump macOS image from deprecated macos-13 to macos-15-intel by {user}`chopan050` in {pr}`4481`
|
||||
* Prepare new release `v0.19.1` and bump minimum required Python version to 3.10 by {user}`behackl` in {pr}`4490`
|
||||
|
||||
|
||||
### Dependency Version Changes
|
||||
|
||||
* Bump typing extensions minimum version by {user}`JasonGrace2282` in {pr}`4121`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4122`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4140`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4148`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4181`
|
||||
* Bump astral-sh/setup-uv from 5 to 6 by {user}`dependabot`[bot] in {pr}`4234`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4204`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4391`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4405`
|
||||
* Bump actions/setup-python from 5 to 6 by {user}`dependabot`[bot] in {pr}`4433`
|
||||
* Bump actions/checkout from 4 to 5 by {user}`dependabot`[bot] in {pr}`4418`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4409`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4460`
|
||||
* [pre-commit.ci] pre-commit autoupdate by {user}`pre-commit-ci`[bot] in {pr}`4467`
|
||||
* Bump github/codeql-action from 3 to 4 by {user}`dependabot`[bot] in {pr}`4466`
|
||||
* Bump astral-sh/setup-uv from 6 to 7 by {user}`dependabot`[bot] in {pr}`4465`
|
||||
* Bump actions/upload-artifact from 4 to 5 by {user}`dependabot`[bot] in {pr}`4464`
|
||||
|
||||
|
||||
## New Contributors
|
||||
* {user}`BenKirkels` made their first contribution in {pr}`4144`
|
||||
* {user}`Hasan-Mesbaul-Ali-Taher` made their first contribution in {pr}`4185`
|
||||
* {user}`ragibson` made their first contribution in {pr}`4217`
|
||||
* {user}`thehugwizard` made their first contribution in {pr}`4202`
|
||||
* {user}`xinoehp512` made their first contribution in {pr}`4226`
|
||||
* {user}`SAYAN02-DEV` made their first contribution in {pr}`4254`
|
||||
* {user}`Akshat-Mishra-py` made their first contribution in {pr}`4251`
|
||||
* {user}`pdrzan` made their first contribution in {pr}`4279`
|
||||
* {user}`pedropxoto` made their first contribution in {pr}`4278`
|
||||
* {user}`giolucasd` made their first contribution in {pr}`4269`
|
||||
* {user}`KaiqueDultra` made their first contribution in {pr}`4276`
|
||||
* {user}`ishu9bansal` made their first contribution in {pr}`4291`
|
||||
* {user}`StevenH34` made their first contribution in {pr}`4213`
|
||||
* {user}`alterdim` made their first contribution in {pr}`4305`
|
||||
* {user}`mohiuddin-khan-shiam` made their first contribution in {pr}`4324`
|
||||
* {user}`elshorbagyx` made their first contribution in {pr}`4332`
|
||||
* {user}`NASAnerd05` made their first contribution in {pr}`4267`
|
||||
* {user}`ra1u` made their first contribution in {pr}`4003`
|
||||
* {user}`AbhilashaTandon` made their first contribution in {pr}`4099`
|
||||
* {user}`nubDotDev` made their first contribution in {pr}`4286`
|
||||
* {user}`haveheartt` made their first contribution in {pr}`4346`
|
||||
* {user}`xnov18` made their first contribution in {pr}`4375`
|
||||
* {user}`Nikil-D-Gr8` made their first contribution in {pr}`3986`
|
||||
* {user}`RBerga06` made their first contribution in {pr}`4398`
|
||||
* {user}`Nikhil172913832` made their first contribution in {pr}`4440`
|
||||
* {user}`SASHAKT1290` made their first contribution in {pr}`4452`
|
||||
* {user}`Brainsucker92` made their first contribution in {pr}`4469`
|
||||
* {user}`NightyStudios` made their first contribution in {pr}`4470`
|
||||
* {user}`Morkunas` made their first contribution in {pr}`4478`
|
||||
* {user}`GuiCT` made their first contribution in {pr}`4479`
|
||||
* {user}`godalming123` made their first contribution in {pr}`4228`
|
||||
* {user}`jakekinchen` made their first contribution in {pr}`4484`
|
||||
|
||||
**Full Changelog**: https://github.com/ManimCommunity/manim/compare/v0.19.0...v0.19.1
|
||||
41
docs/source/changelog/0.19.2-changelog.md
Normal file
41
docs/source/changelog/0.19.2-changelog.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
short-title: v0.19.2
|
||||
description: Changelog for Manim v0.19.2
|
||||
---
|
||||
|
||||
# v0.19.2
|
||||
|
||||
Date
|
||||
: January 17, 2026
|
||||
|
||||
|
||||
## What's Changed
|
||||
### Highlights 🌟
|
||||
* Add support for Python 3.14, bump minimum Python to 3.11 and av to 14.0.1 by {user}`behackl` in {pr}`4385`
|
||||
|
||||
### Bug Fixes 🐛
|
||||
* Fix argument passed to `get_hash_from_play_call` in hashing by {user}`judenimo` in {pr}`4524`
|
||||
* Fix incorrect `Circle.point_at_angle` calculation by {user}`Swarnlataaa` in {pr}`4438`
|
||||
|
||||
### Testing 🧪
|
||||
* Test on Apple Silicon ARM64 by {user}`cclauss` in {pr}`4496`
|
||||
|
||||
### Code Quality & Refactoring 🧹
|
||||
* Add ruff rules PERF for performance by {user}`cclauss` in {pr}`4492`
|
||||
* Remove deprecation warning from pytest "np.trapz" -> "np.trapezoid" by {user}`henrikmidtiby` in {pr}`4513`
|
||||
* Bump Python target versions of both mypy and ruff by {user}`behackl` in {pr}`4520`
|
||||
* Replace legacy numpy usage -- ruff rule NPY002 by {user}`cclauss` in {pr}`4516`
|
||||
* Add `.github/release.yml` for improved classifications in automatically generated changelogs by {user}`behackl` in {pr}`4526`
|
||||
* Check and bump lower version requirements for dependencies by {user}`henrikmidtiby` in {pr}`4529`
|
||||
|
||||
### Type Hints 📝
|
||||
* Add type annotations to `three_dimensions.py` by {user}`henrikmidtiby` in {pr}`4497`
|
||||
|
||||
### Other Changes
|
||||
* Prepare new release `v0.19.2` by {user}`behackl` in {pr}`4528`
|
||||
|
||||
## New Contributors
|
||||
* {user}`judenimo` made their first contribution in {pr}`4524`
|
||||
* {user}`Swarnlataaa` made their first contribution in {pr}`4438`
|
||||
|
||||
**Full Changelog**: https://github.com/ManimCommunity/manim/compare/v0.19.1...v0.19.2
|
||||
86
docs/source/changelog/0.20.0-changelog.md
Normal file
86
docs/source/changelog/0.20.0-changelog.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
short-title: v0.20.0
|
||||
description: Changelog for v0.20.0
|
||||
---
|
||||
|
||||
# v0.20.0
|
||||
|
||||
Date
|
||||
: February 20, 2026
|
||||
|
||||
|
||||
## What's Changed
|
||||
### Breaking Changes 🚨
|
||||
* Fix `ImageMobject` 3D rotation/flipping and remove resampling algorithms `lanczos` (`antialias`), `box` and `hamming` by {user}`chopan050` in {pr}`4266`
|
||||
* Fix `YELLOW_C` and add `PURE_CYAN`, `PURE_MAGENTA` and `PURE_YELLOW` by {user}`chopan050` in {pr}`4562`
|
||||
|
||||
### Highlights 🌟
|
||||
* Rewrite MathTex to make it more robust regarding splitting by {user}`henrikmidtiby` in {pr}`4515`
|
||||
|
||||
The MathTex implementation has been updated to make it more robust and fix a number of issues.
|
||||
A beneficial side effect is that named groups in svg files can now be accessed through SVGMobject.
|
||||
|
||||
* Add new Animation Builder `Mobject.always` by {user}`JasonGrace2282` in {pr}`4594`
|
||||
|
||||
This new feature is a convenience wrapper around `add_updater` that allows adding
|
||||
updaters to a mobject in an intuitive and easy-to-read way. Example usage in a scene:
|
||||
```python
|
||||
d = Dot()
|
||||
s = Square()
|
||||
d.always.next_to(s, UP)
|
||||
self.add(s, d)
|
||||
self.play(s.animate.to_edge(LEFT))
|
||||
```
|
||||
|
||||
|
||||
### New Features ✨
|
||||
* Add a `seed` config option + `--seed` CLI option for reproducible randomness in rendered scenes by {user}`arnaud-ma` in {pr}`4532`
|
||||
|
||||
### Enhancements 🚀
|
||||
* Enable `strict=True` for `zip()` where safe by {user}`Oll-iver` in {pr}`4547`
|
||||
|
||||
### Bug Fixes 🐛
|
||||
* using `color` instead of `fill_color` with MathTeX for node labels by {user}`Schefflera-Arboricola` in {pr}`4501`
|
||||
* fix: infinite recursion caused by accessing color of a highlighted Ta… by {user}`BHearron` in {pr}`4435`
|
||||
* Prevent potential `UnboundLocalError` in `PolarPlane` by {user}`RinZ27` in {pr}`4557`
|
||||
* Fixed division by 0 in `turn_animation_into_updater` by {user}`SoldierSacha` in {pr}`4567`
|
||||
* Fix TOCTOU Race Conditions when creating directories by {user}`SoldierSacha` in {pr}`4587`
|
||||
* Resolve more race conditions potentially happening during directory creation by {user}`SoldierSacha` in {pr}`4589`
|
||||
* Fix `c2p`/`coords_to_point` method call with single flat list or 1D array input by {user}`danielalanbates` in {pr}`4596`
|
||||
|
||||
### Documentation 📚
|
||||
* Enable rendered documentation of `RandomColorGenerator` by {user}`arnaud-ma` in {pr}`4533`
|
||||
* Remove pin to Python 3.13 in installation docs by {user}`chopan050` in {pr}`4534`
|
||||
* Fix broken aquabeam OpenGL link using Wayback Machine by {user}`behackl` in {pr}`4545`
|
||||
* Add type annotations and docstrings in `opengl_renderer.py` by {user}`arnaud-ma` in {pr}`4537`
|
||||
* docs: improve `TransformFromCopy` docstring by {user}`GoThrones` in {pr}`4597`
|
||||
|
||||
### Infrastructure & Build 🔨
|
||||
* Install missing dependencies in release pipeline by {user}`behackl` in {pr}`4531`
|
||||
|
||||
### Code Quality & Refactoring 🧹
|
||||
* Rework and consolidate release changelog script, add previously skipped changelog entries by {user}`behackl` in {pr}`4568`
|
||||
* Remove `__future__.annotations` from required imports by {user}`JasonGrace2282` in {pr}`4571`
|
||||
* Cleaned up `mypy.ini` by {user}`henrikmidtiby` in {pr}`4584`
|
||||
* Add `py.typed` to declare manim as having type hints by {user}`Timmmm` in {pr}`4553`
|
||||
* Fix assertion in `ImageMobjectFromCamera.interpolate_color()` by {user}`chopan050` in {pr}`4593`
|
||||
* Reduce dependency on scipy - replace `scipy.special.comb` with `math.comb` by {user}`fmuenkel` in {pr}`4598`
|
||||
|
||||
### Type Hints 📝
|
||||
* Add type annotations to `rotation.py` by {user}`fmuenkel` in {pr}`4535`
|
||||
* Add type annotations to `opengl_compatibility.py` by {user}`fmuenkel` in {pr}`4585`
|
||||
* Add type annotations to `image_mobject.py` by {user}`henrikmidtiby` in {pr}`4458`
|
||||
* Add type annotations to `opengl_image_mobject.py` by {user}`fmuenkel` in {pr}`4536`
|
||||
* Add type annotations to `point_cloud_mobject.py` by {user}`fmuenkel` in {pr}`4586`
|
||||
|
||||
## New Contributors
|
||||
* {user}`arnaud-ma` made their first contribution in {pr}`4533`
|
||||
* {user}`Schefflera-Arboricola` made their first contribution in {pr}`4501`
|
||||
* {user}`BHearron` made their first contribution in {pr}`4435`
|
||||
* {user}`RinZ27` made their first contribution in {pr}`4557`
|
||||
* {user}`SoldierSacha` made their first contribution in {pr}`4567`
|
||||
* {user}`Oll-iver` made their first contribution in {pr}`4547`
|
||||
* {user}`GoThrones` made their first contribution in {pr}`4597`
|
||||
* {user}`danielalanbates` made their first contribution in {pr}`4596`
|
||||
|
||||
**Full Changelog**: [Compare view](https://github.com/ManimCommunity/manim/compare/v0.19.2...v0.20.0)
|
||||
41
docs/source/changelog/0.20.1-changelog.md
Normal file
41
docs/source/changelog/0.20.1-changelog.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
short-title: v0.20.1
|
||||
description: Changelog for v0.20.1
|
||||
---
|
||||
|
||||
# v0.20.1
|
||||
|
||||
Date
|
||||
: February 27, 2026
|
||||
|
||||
|
||||
## What's Changed
|
||||
### Enhancements 🚀
|
||||
* Cleanup `TipableVMobject`: avoid mutable default and fix `assign_tip_attr` typo by {user}`josiest` in {pr}`4503`
|
||||
* enhancement: optimize Docker image build and runtime footprint by {user}`behackl` in {pr}`4604`
|
||||
|
||||
### Bug Fixes 🐛
|
||||
* fix: MathTex double-brace splitting no longer fires on natural LaTeX `}}` by {user}`behackl` in {pr}`4602`
|
||||
* Fix creation or animation of a zero-length `DashedLine` by {user}`SORVER` in {pr}`4606`
|
||||
* Fix moving-object detection for nested AnimationGroups with z-indexed mobjects by {user}`Merzlikin-Matvey` in {pr}`4389`
|
||||
* Fix unintended propagation of `kwargs` in `LaggedStartMap` by {user}`irvanalhaq9` in {pr}`4613`
|
||||
|
||||
### Documentation 📚
|
||||
* Documentation: manual installation of manim as a local package by {user}`u7920349` in {pr}`4456`
|
||||
* Add alt text to all images in `README.md` by {user}`VerisimilitudeX` in {pr}`4064`
|
||||
|
||||
### Code Quality & Refactoring 🧹
|
||||
* Fix publish release workflow by {user}`behackl` in {pr}`4600`
|
||||
* Silence pydub ffmpeg/avconv import warning when ffmpeg CLI is absent by {user}`behackl` in {pr}`4603`
|
||||
|
||||
### Type Hints 📝
|
||||
* Add type annotations to `manim/_config/utils.py` by {user}`henrikmidtiby` in {pr}`4230`
|
||||
|
||||
## New Contributors
|
||||
* {user}`SORVER` made their first contribution in {pr}`4606`
|
||||
* {user}`josiest` made their first contribution in {pr}`4503`
|
||||
* {user}`u7920349` made their first contribution in {pr}`4456`
|
||||
* {user}`Merzlikin-Matvey` made their first contribution in {pr}`4389`
|
||||
* {user}`VerisimilitudeX` made their first contribution in {pr}`4064`
|
||||
|
||||
**Full Changelog**: [Compare view](https://github.com/ManimCommunity/manim/compare/v0.20.0...v0.20.1)
|
||||
|
|
@ -8,9 +8,11 @@ from __future__ import annotations
|
|||
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import manim
|
||||
from manim.utils.docbuild.module_parsing import parse_module_attributes
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
|
|
@ -24,7 +26,7 @@ sys.path.insert(0, os.path.abspath("."))
|
|||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "Manim"
|
||||
copyright = "2020-2022, The Manim Community Dev Team"
|
||||
copyright = f"2020-{datetime.now().year}, The Manim Community Dev Team" # noqa: A001
|
||||
author = "The Manim Community Dev Team"
|
||||
|
||||
|
||||
|
|
@ -44,17 +46,36 @@ extensions = [
|
|||
"sphinxext.opengraph",
|
||||
"manim.utils.docbuild.manim_directive",
|
||||
"manim.utils.docbuild.autocolor_directive",
|
||||
"manim.utils.docbuild.autoaliasattr_directive",
|
||||
"sphinx.ext.graphviz",
|
||||
"sphinx.ext.inheritance_diagram",
|
||||
"sphinxcontrib.programoutput",
|
||||
"myst_parser",
|
||||
"sphinx_design",
|
||||
"sphinx_reredirects",
|
||||
]
|
||||
|
||||
# Automatically generate stub pages when using the .. autosummary directive
|
||||
autosummary_generate = True
|
||||
|
||||
myst_enable_extensions = ["colon_fence", "amsmath", "deflist"]
|
||||
|
||||
# redirects (for moved / deleted pages)
|
||||
redirects = {
|
||||
"installation/linux": "uv.html",
|
||||
"installation/macos": "uv.html",
|
||||
"installation/windows": "uv.html",
|
||||
}
|
||||
|
||||
# generate documentation from type hints
|
||||
ALIAS_DOCS_DICT = parse_module_attributes()[0]
|
||||
autodoc_typehints = "description"
|
||||
autodoc_type_aliases = {
|
||||
alias_name: f"~manim.{module}.{alias_name}"
|
||||
for module, module_dict in ALIAS_DOCS_DICT.items()
|
||||
for category_dict in module_dict.values()
|
||||
for alias_name in category_dict
|
||||
}
|
||||
autoclass_content = "both"
|
||||
|
||||
# controls whether functions documented by the autofunction directive
|
||||
|
|
@ -103,7 +124,6 @@ html_theme_options = {
|
|||
"source_repository": "https://github.com/ManimCommunity/manim/",
|
||||
"source_branch": "main",
|
||||
"source_directory": "docs/source/",
|
||||
"top_of_page_button": None,
|
||||
"light_logo": "manim-logo-sidebar.svg",
|
||||
"dark_logo": "manim-logo-sidebar-dark.svg",
|
||||
"light_css_variables": {
|
||||
|
|
@ -136,11 +156,13 @@ html_title = f"Manim Community v{manim.__version__}"
|
|||
# This specifies any additional css files that will override the theme's
|
||||
html_css_files = ["custom.css"]
|
||||
|
||||
latex_engine = "lualatex"
|
||||
|
||||
# external links
|
||||
extlinks = {
|
||||
"issue": ("https://github.com/ManimCommunity/manim/issues/%s", "#%s"),
|
||||
"pr": ("https://github.com/ManimCommunity/manim/pull/%s", "#%s"),
|
||||
"pr": ("https://github.com/ManimCommunity/manim/pull/%s", "PR #%s"),
|
||||
"user": ("https://github.com/%s", "@%s"),
|
||||
}
|
||||
|
||||
# opengraph settings
|
||||
|
|
|
|||
|
|
@ -38,14 +38,10 @@ To get an overview of what our community is currently working on, check out
|
|||
Contributing can be confusing, so here are a few guides:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 3
|
||||
|
||||
contributing/development
|
||||
contributing/docstrings
|
||||
contributing/references
|
||||
contributing/examples
|
||||
contributing/typings
|
||||
contributing/admonitions
|
||||
contributing/docs
|
||||
contributing/testing
|
||||
contributing/performance
|
||||
contributing/internationalization
|
||||
|
|
|
|||
249
docs/source/contributing/development.md
Normal file
249
docs/source/contributing/development.md
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
# Manim Development Process
|
||||
|
||||
## For first-time contributors
|
||||
|
||||
1. Install git:
|
||||
|
||||
For instructions see <https://git-scm.com/>.
|
||||
|
||||
2. Fork the project:
|
||||
|
||||
Go to <https://github.com/ManimCommunity/manim> and click the "fork" button
|
||||
to create a copy of the project for you to work on. You will need a
|
||||
GitHub account. This will allow you to make a "Pull Request" (PR)
|
||||
to the ManimCommunity repo later on.
|
||||
|
||||
3. Clone your fork to your local computer:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/<your-username>/manim.git
|
||||
```
|
||||
|
||||
GitHub will provide both a SSH (`git@github.com:<your-username>/manim.git`) and
|
||||
HTTPS (`https://github.com/<your-username>/manim.git`) URL for cloning.
|
||||
You can use SSH if you have SSH keys setup.
|
||||
|
||||
:::{WARNING}
|
||||
Do not clone the ManimCommunity repository. You must clone your own
|
||||
fork.
|
||||
:::
|
||||
|
||||
4. Change the directory to enter the project folder:
|
||||
|
||||
```shell
|
||||
cd manim
|
||||
```
|
||||
|
||||
5. Add the upstream repository, ManimCommunity:
|
||||
|
||||
```shell
|
||||
git remote add upstream https://github.com/ManimCommunity/manim.git
|
||||
```
|
||||
|
||||
6. Now, `git remote -v` should show two remote repositories named:
|
||||
|
||||
- `origin`, your forked repository
|
||||
- `upstream` the ManimCommunity repository
|
||||
|
||||
7. Install the Python project management tool `uv`, as recommended
|
||||
in our {doc}`installation guide for users </installation/uv>`.
|
||||
|
||||
8. Let `uv` create a virtual environment for your development
|
||||
installation by running
|
||||
|
||||
```shell
|
||||
uv sync
|
||||
```
|
||||
|
||||
In case you need (or want) to install some of the optional dependency
|
||||
groups defined in our [`pyproject.toml`](https://github.com/ManimCommunity/manim/blob/main/pyproject.toml),
|
||||
run `uv sync --all-extras`, or pass the `--extra` flag with the
|
||||
name of a group, for example `uv sync --extra jupyterhub`.
|
||||
|
||||
9. Install Pre-Commit:
|
||||
|
||||
```shell
|
||||
uv run pre-commit install
|
||||
```
|
||||
|
||||
This will ensure during development that each of your commits is properly
|
||||
formatted against our linter and formatters.
|
||||
|
||||
You are now ready to work on Manim!
|
||||
|
||||
## Develop your contribution
|
||||
|
||||
1. Checkout your local repository's main branch and pull the latest
|
||||
changes from ManimCommunity, `upstream`, into your local repository:
|
||||
|
||||
```shell
|
||||
git switch main
|
||||
git pull --rebase upstream main
|
||||
```
|
||||
|
||||
2. Create a branch for the changes you want to work on rather than working
|
||||
off of your local main branch:
|
||||
|
||||
```shell
|
||||
git switch -c <new branch name> upstream/main
|
||||
```
|
||||
|
||||
This ensures you can easily update your local repository's main with the
|
||||
first step and switch branches to work on multiple features.
|
||||
|
||||
3. Write some awesome code!
|
||||
|
||||
You're ready to make changes in your local repository's branch.
|
||||
You can add local files you've changed within the current directory with
|
||||
`git add .`, or add specific files with
|
||||
|
||||
```shell
|
||||
git add <file/directory>
|
||||
```
|
||||
|
||||
and commit these changes to your local history with `git commit`. If you
|
||||
have installed pre-commit, your commit will succeed only if none of the
|
||||
hooks fail.
|
||||
|
||||
:::{tip}
|
||||
When crafting commit messages, it is highly recommended that
|
||||
you adhere to [these guidelines](https://www.conventionalcommits.org/en/v1.0.0/).
|
||||
:::
|
||||
|
||||
4. Add new or update existing tests.
|
||||
|
||||
Depending on your changes, you may need to update or add new tests. For new
|
||||
features, it is required that you include tests with your PR. Details of
|
||||
our testing system are explained in the {doc}`testing guide <testing>`.
|
||||
|
||||
5. Update docstrings and documentation:
|
||||
|
||||
Update the docstrings (the text in triple quotation marks) of any functions
|
||||
or classes you change and include them with any new functions you add.
|
||||
See the {doc}`documentation guide <docs/docstrings>` for more information about how we
|
||||
prefer our code to be documented. The content of the docstrings will be
|
||||
rendered in the {doc}`reference manual <../reference>`.
|
||||
|
||||
:::{tip}
|
||||
Use the {mod}`manim directive for Sphinx <manim.utils.docbuild.manim_directive>` to add examples
|
||||
to the documentation!
|
||||
:::
|
||||
|
||||
As far as development on your local machine goes, these are the main steps you
|
||||
should follow.
|
||||
|
||||
(polishing-changes-and-submitting-a-pull-request)=
|
||||
|
||||
## Polishing Changes and Submitting a Pull Request
|
||||
|
||||
As soon as you are ready to share your local changes with the community
|
||||
so that they can be discussed, go through the following steps to open a
|
||||
pull request. A pull request signifies to the ManimCommunity organization,
|
||||
"Here are some changes I wrote; I think it's worthwhile for you to maintain
|
||||
them."
|
||||
|
||||
:::{note}
|
||||
You do not need to have everything (code/documentation/tests) complete
|
||||
to open a pull request (PR). If the PR is still under development, please
|
||||
mark it as a draft. Community developers will still be able to review the
|
||||
changes, discuss yet-to-be-implemented changes, and offer advice; however,
|
||||
the more complete your PR, the quicker it will be merged.
|
||||
:::
|
||||
|
||||
1. Update your fork on GitHub to reflect your local changes:
|
||||
|
||||
```shell
|
||||
git push -u origin <branch name>
|
||||
```
|
||||
|
||||
Doing so creates a new branch on your remote fork, `origin`, with the
|
||||
contents of your local repository on GitHub. In subsequent pushes, this
|
||||
local branch will track the branch `origin` and `git push` is enough.
|
||||
|
||||
2. Make a pull request (PR) on GitHub.
|
||||
|
||||
In order to make the ManimCommunity development team aware of your changes,
|
||||
you can make a PR to the ManimCommunity repository from your fork.
|
||||
|
||||
:::{WARNING}
|
||||
Make sure to select `ManimCommunity/manim` instead of `3b1b/manim`
|
||||
as the base repository!
|
||||
:::
|
||||
|
||||
Choose the branch from your fork as the head repository - see the
|
||||
screenshot below.
|
||||
|
||||
```{image} /_static/pull-requests.png
|
||||
:align: center
|
||||
```
|
||||
|
||||
Please make sure you follow the template (this is the default
|
||||
text you are shown when first opening the 'New Pull Request' page).
|
||||
|
||||
Your changes are eligible to be merged if:
|
||||
|
||||
1. there are no merge conflicts
|
||||
2. the tests in our pipeline pass
|
||||
3. at least one (two for more complex changes) Community Developer approves the changes
|
||||
|
||||
You can check for merge conflicts between the current upstream/main and
|
||||
your branch by executing `git pull upstream main` locally. If this
|
||||
generates any merge conflicts, you need to resolve them and push an
|
||||
updated version of the branch to your fork of the repository.
|
||||
|
||||
Our pipeline consists of a series of different tests that ensure
|
||||
that Manim still works as intended and that the code you added
|
||||
sticks to our coding conventions.
|
||||
|
||||
- **Code style**: We use the code style imposed
|
||||
by [Black](https://black.readthedocs.io/en/stable/), [isort](https://pycqa.github.io/isort/)
|
||||
and [flake8](https://flake8.pycqa.org/en/latest/). The GitHub pipeline
|
||||
makes sure that the (Python) files changed in your pull request
|
||||
also adhere to this code style. If this step of the pipeline fails,
|
||||
fix your code formatting automatically by running `black <file or directory>` and `isort <file or directory>`.
|
||||
To fix code style problems, run `flake8 <file or directory>` for a style report, and then fix the problems
|
||||
manually that were detected by `flake8`.
|
||||
- **Tests**: The pipeline runs Manim's test suite on different operating systems
|
||||
(the latest versions of Ubuntu, macOS, and Windows) for different versions of Python.
|
||||
The test suite consists of two different kinds of tests: integration tests
|
||||
and doctests. You can run them locally by executing `uv run pytest`
|
||||
and `uv run pytest --doctest-modules manim`, respectively, from the
|
||||
root directory of your cloned fork.
|
||||
- **Documentation**: We also build a version of the documentation corresponding
|
||||
to your pull request. Make sure not to introduce any Sphinx errors, and have
|
||||
a look at the built HTML files to see whether the formatting of the documentation
|
||||
you added looks as you intended. You can build the documentation locally
|
||||
by running `make html` from the `docs` directory. Make sure you have [Graphviz](https://graphviz.org/)
|
||||
installed locally in order to build the inheritance diagrams. See {doc}`docs` for
|
||||
more information.
|
||||
|
||||
Finally, if the pipeline passes and you are satisfied with your changes: wait for
|
||||
feedback and iterate over any requested changes. You will likely be asked to
|
||||
edit or modify your PR in one way or another during this process. This is not
|
||||
an indictment of your work, but rather a strong signal that the community
|
||||
wants to merge your changes! Once approved, your changes may be merged!
|
||||
|
||||
### Further useful guidelines
|
||||
|
||||
1. When submitting a PR, please mention explicitly if it includes breaking changes.
|
||||
2. When submitting a PR, make sure that your proposed changes are as general as
|
||||
possible, and ready to be taken advantage of by all of Manim's users. In
|
||||
particular, leave out any machine-specific configurations, or any personal
|
||||
information it may contain.
|
||||
3. If you are a maintainer, please label issues and PRs appropriately and
|
||||
frequently.
|
||||
4. When opening a new issue, if there are old issues that are related, add a link
|
||||
to them in your new issue (even if the old ones are closed).
|
||||
5. When submitting a code review, it is highly recommended that you adhere to
|
||||
[these general guidelines](https://conventionalcomments.org/).
|
||||
6. If you find stale or inactive issues that seem to be irrelevant, please post
|
||||
a comment saying 'This issue should be closed', and a community developer
|
||||
will take a look.
|
||||
7. Please do as much as possible to keep issues, PRs, and development in
|
||||
general as tidy as possible.
|
||||
|
||||
You can find examples for the `docs` in several places:
|
||||
the {doc}`Example Gallery <../examples>`, {doc}`Tutorials <../tutorials/index>`,
|
||||
and {doc}`Reference Classes <../reference>`.
|
||||
|
||||
**Thank you for contributing!**
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
=========================
|
||||
Manim Development Process
|
||||
=========================
|
||||
|
||||
For first-time contributors
|
||||
---------------------------
|
||||
#. Install git:
|
||||
|
||||
For instructions see https://git-scm.com/.
|
||||
|
||||
|
||||
#. Fork the project:
|
||||
|
||||
Go to https://github.com/ManimCommunity/manim and click the "fork" button
|
||||
to create a copy of the project for you to work on. You will need a
|
||||
GitHub account. This will allow you to make a "Pull Request" (PR)
|
||||
to the ManimCommunity repo later on.
|
||||
|
||||
#. Clone your fork to your local computer:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://github.com/<your-username>/manim.git
|
||||
|
||||
GitHub will provide both a SSH (``git@github.com:<your-username>/manim.git``) and
|
||||
HTTPS (``https://github.com/<your-username>/manim.git``) URL for cloning.
|
||||
You can use SSH if you have SSH keys setup.
|
||||
|
||||
.. WARNING::
|
||||
|
||||
Do not clone the ManimCommunity repository. You must clone your own
|
||||
fork.
|
||||
|
||||
#. Change the directory to enter the project folder:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cd manim
|
||||
|
||||
#. Add the upstream repository, ManimCommunity:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git remote add upstream https://github.com/ManimCommunity/manim.git
|
||||
|
||||
#. Now, ``git remote -v`` should show two remote repositories named:
|
||||
|
||||
- ``origin``, your forked repository
|
||||
- ``upstream`` the ManimCommunity repository
|
||||
|
||||
#. Install Manim:
|
||||
|
||||
- Follow the steps in our :doc:`installation instructions
|
||||
<../installation>` to install **Manim's dependencies**,
|
||||
primarily ``ffmpeg`` and ``LaTeX``.
|
||||
|
||||
- We recommend using `Poetry <https://python-poetry.org>`__ to manage your
|
||||
developer installation of Manim. Poetry is a tool for dependency
|
||||
management and packaging in Python. It allows you to declare the libraries
|
||||
your project depends on, and it will manage (install / update) them
|
||||
for you. In addition, Poetry provides a simple interface for
|
||||
managing virtual environments.
|
||||
|
||||
If you choose to use Poetry as well, follow `Poetry's installation
|
||||
guidelines <https://python-poetry.org/docs/master/#installation>`__
|
||||
to install it on your system, then run ``poetry install`` from
|
||||
your cloned repository. Poetry will then install Manim, as well
|
||||
as create and enter a virtual environment. You can always re-enter
|
||||
that environment by running ``poetry shell``.
|
||||
|
||||
- In case you want to install extra dependencies that are defined in
|
||||
the ``[tool.poetry.extras]`` section of ``pyproject.toml``, this can be done by passing
|
||||
the ``-E`` flag, for example ``poetry install -E jupyterlab -E gui``.
|
||||
|
||||
- In case you decided against Poetry, you can install Manim via pip
|
||||
by running ``python3 -m pip install .``. Note that due to our
|
||||
development infrastructure being based on Poetry, we currently
|
||||
do not support editable installs via ``pip``, so you will have
|
||||
to re-run this command every time you make changes to the source
|
||||
code.
|
||||
|
||||
.. note::
|
||||
|
||||
The following steps assume that you chose to install and work with
|
||||
Poetry.
|
||||
|
||||
#. Install Pre-Commit:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
poetry run pre-commit install
|
||||
|
||||
This will ensure during development that each of your commits is properly
|
||||
formatted against our linter and formatters, ``black``, ``flake8``,
|
||||
``isort`` and ``codespell``.
|
||||
|
||||
You are now ready to work on Manim!
|
||||
|
||||
Develop your contribution
|
||||
-------------------------
|
||||
|
||||
#. Checkout your local repository's main branch and pull the latest
|
||||
changes from ManimCommunity, ``upstream``, into your local repository:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git checkout main
|
||||
git pull --rebase upstream main
|
||||
|
||||
#. Create a branch for the changes you want to work on rather than working
|
||||
off of your local main branch:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git checkout -b <new branch name> upstream/main
|
||||
|
||||
This ensures you can easily update your local repository's main with the
|
||||
first step and switch branches to work on multiple features.
|
||||
|
||||
#. Write some awesome code!
|
||||
|
||||
You're ready to make changes in your local repository's branch.
|
||||
You can add local files you've changed within the current directory with
|
||||
``git add .``, or add specific files with
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git add <file/directory>
|
||||
|
||||
and commit these changes to your local history with ``git commit``. If you
|
||||
have installed pre-commit, your commit will succeed only if none of the
|
||||
hooks fail.
|
||||
|
||||
.. tip::
|
||||
|
||||
When crafting commit messages, it is highly recommended that
|
||||
you adhere to `these guidelines <https://www.conventionalcommits.org/en/v1.0.0/>`_.
|
||||
|
||||
#. Add new or update existing tests.
|
||||
|
||||
Depending on your changes, you may need to update or add new tests. For new
|
||||
features, it is required that you include tests with your PR. Details of
|
||||
our testing system are explained in the :doc:`testing guide <testing>`.
|
||||
|
||||
|
||||
#. Update docstrings and documentation:
|
||||
|
||||
Update the docstrings (the text in triple quotation marks) of any functions
|
||||
or classes you change and include them with any new functions you add.
|
||||
See the :doc:`documentation guide <docstrings>` for more information about how we
|
||||
prefer our code to be documented. The content of the docstrings will be
|
||||
rendered in the :doc:`reference manual <../reference>`.
|
||||
|
||||
.. tip::
|
||||
|
||||
Use the :mod:`manim directive for Sphinx <manim.utils.docbuild.manim_directive>` to add examples
|
||||
to the documentation!
|
||||
|
||||
As far as development on your local machine goes, these are the main steps you
|
||||
should follow.
|
||||
|
||||
Polishing Changes and Submitting a Pull Request
|
||||
-----------------------------------------------
|
||||
|
||||
As soon as you are ready to share your local changes with the community
|
||||
so that they can be discussed, go through the following steps to open a
|
||||
pull request. A pull request signifies to the ManimCommunity organization,
|
||||
"Here are some changes I wrote; I think it's worthwhile for you to maintain
|
||||
them."
|
||||
|
||||
.. note::
|
||||
|
||||
You do not need to have everything (code/documentation/tests) complete
|
||||
to open a pull request (PR). If the PR is still under development, please
|
||||
mark it as a draft. Community developers will still be able to review the
|
||||
changes, discuss yet-to-be-implemented changes, and offer advice; however,
|
||||
the more complete your PR, the quicker it will be merged.
|
||||
|
||||
#. Update your fork on GitHub to reflect your local changes:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git push -u origin <branch name>
|
||||
|
||||
Doing so creates a new branch on your remote fork, ``origin``, with the
|
||||
contents of your local repository on GitHub. In subsequent pushes, this
|
||||
local branch will track the branch ``origin`` and ``git push`` is enough.
|
||||
|
||||
|
||||
#. Make a pull request (PR) on GitHub.
|
||||
|
||||
In order to make the ManimCommunity development team aware of your changes,
|
||||
you can make a PR to the ManimCommunity repository from your fork.
|
||||
|
||||
.. WARNING::
|
||||
|
||||
Make sure to select ``ManimCommunity/manim`` instead of ``3b1b/manim``
|
||||
as the base repository!
|
||||
|
||||
Choose the branch from your fork as the head repository - see the
|
||||
screenshot below.
|
||||
|
||||
.. image:: /_static/pull-requests.png
|
||||
:align: center
|
||||
|
||||
Please make sure you follow the template (this is the default
|
||||
text you are shown when first opening the 'New Pull Request' page).
|
||||
|
||||
|
||||
Your changes are eligible to be merged if:
|
||||
|
||||
#. there are no merge conflicts
|
||||
#. the tests in our pipeline pass
|
||||
#. at least one (two for more complex changes) Community Developer approves the changes
|
||||
|
||||
You can check for merge conflicts between the current upstream/main and
|
||||
your branch by executing ``git pull upstream main`` locally. If this
|
||||
generates any merge conflicts, you need to resolve them and push an
|
||||
updated version of the branch to your fork of the repository.
|
||||
|
||||
Our pipeline consists of a series of different tests that ensure
|
||||
that Manim still works as intended and that the code you added
|
||||
sticks to our coding conventions.
|
||||
|
||||
- **Code style**: We use the code style imposed
|
||||
by `Black <https://black.readthedocs.io/en/stable/>`_, `isort <https://pycqa.github.io/isort/>`_
|
||||
and `flake8 <https://flake8.pycqa.org/en/latest/>`_. The GitHub pipeline
|
||||
makes sure that the (Python) files changed in your pull request
|
||||
also adhere to this code style. If this step of the pipeline fails,
|
||||
fix your code formatting automatically by running ``black <file or directory>`` and ``isort <file or directory>``.
|
||||
To fix code style problems, run ``flake8 <file or directory>`` for a style report, and then fix the problems
|
||||
manually that were detected by ``flake8``.
|
||||
|
||||
- **Tests**: The pipeline runs Manim's test suite on different operating systems
|
||||
(the latest versions of Ubuntu, macOS, and Windows) for different versions of Python.
|
||||
The test suite consists of two different kinds of tests: integration tests
|
||||
and doctests. You can run them locally by executing ``poetry run pytest``
|
||||
and ``poetry run pytest --doctest-modules manim``, respectively, from the
|
||||
root directory of your cloned fork.
|
||||
|
||||
- **Documentation**: We also build a version of the documentation corresponding
|
||||
to your pull request. Make sure not to introduce any Sphinx errors, and have
|
||||
a look at the built HTML files to see whether the formatting of the documentation
|
||||
you added looks as you intended. You can build the documentation locally
|
||||
by running ``make html`` from the ``docs`` directory. Make sure you have `Graphviz <https://graphviz.org/>`_
|
||||
installed locally in order to build the inheritance diagrams.
|
||||
|
||||
Finally, if the pipeline passes and you are satisfied with your changes: wait for
|
||||
feedback and iterate over any requested changes. You will likely be asked to
|
||||
edit or modify your PR in one way or another during this process. This is not
|
||||
an indictment of your work, but rather a strong signal that the community
|
||||
wants to merge your changes! Once approved, your changes may be merged!
|
||||
|
||||
Further useful guidelines
|
||||
=========================
|
||||
|
||||
#. When submitting a PR, please mention explicitly if it includes breaking changes.
|
||||
|
||||
#. When submitting a PR, make sure that your proposed changes are as general as
|
||||
possible, and ready to be taken advantage of by all of Manim's users. In
|
||||
particular, leave out any machine-specific configurations, or any personal
|
||||
information it may contain.
|
||||
|
||||
#. If you are a maintainer, please label issues and PRs appropriately and
|
||||
frequently.
|
||||
|
||||
#. When opening a new issue, if there are old issues that are related, add a link
|
||||
to them in your new issue (even if the old ones are closed).
|
||||
|
||||
#. When submitting a code review, it is highly recommended that you adhere to
|
||||
`these general guidelines <https://conventionalcomments.org/>`_.
|
||||
|
||||
#. If you find stale or inactive issues that seem to be irrelevant, please post
|
||||
a comment saying 'This issue should be closed', and a community developer
|
||||
will take a look.
|
||||
|
||||
#. Please do as much as possible to keep issues, PRs, and development in
|
||||
general as tidy as possible.
|
||||
|
||||
|
||||
You can find examples for the ``docs`` in several places:
|
||||
the :doc:`Example Gallery <../examples>`, :doc:`Tutorials <../tutorials/index>`,
|
||||
and :doc:`Reference Classes <../reference>`.
|
||||
|
||||
**Thank you for contributing!**
|
||||
84
docs/source/contributing/docs.rst
Normal file
84
docs/source/contributing/docs.rst
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
====================
|
||||
Adding Documentation
|
||||
====================
|
||||
|
||||
Building the documentation
|
||||
--------------------------
|
||||
|
||||
When you clone the Manim repository from GitHub, you can access the
|
||||
``docs/`` folder which contains the necessary files to build the
|
||||
documentation.
|
||||
|
||||
To build the docs locally, open a CLI, enter the ``docs/`` folder with the
|
||||
``cd`` command and execute the following depending on your OS:
|
||||
|
||||
- Windows: ``./make.bat html``
|
||||
- macOS and Linux: ``make html``
|
||||
|
||||
The first time you build the docs, the process will take several
|
||||
minutes because it needs to generate all the ``.rst`` (reST:
|
||||
reStructured Text) files from scratch by reading and parsing all the
|
||||
Manim content. The process becomes much shorter the next time, as it
|
||||
rebuilds only the parts which have changed.
|
||||
|
||||
|
||||
Sphinx library and extensions
|
||||
-----------------------------
|
||||
|
||||
Manim uses `Sphinx <https://www.sphinx-doc.org>`_ for building the
|
||||
docs. It also makes use of Sphinx extensions such as:
|
||||
|
||||
- `Autodoc: <https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html>`_
|
||||
imports Manim's Python source code, extracts its docstrings and
|
||||
generates documentation from them.
|
||||
- `Autosummary: <https://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html>`_
|
||||
a complement to Autodoc which adds a special directive ``autosummary``,
|
||||
used in Manim to automatically document classes, methods,
|
||||
attributes, functions, module-level variables and exceptions.
|
||||
Autosummary makes use of `Jinja templates <https://jinja.palletsprojects.com/templates/>`_,
|
||||
which Manim defines for autosummarizing classes and modules
|
||||
inside ``docs/source/_templates/``.
|
||||
- `Graphviz extension for Sphinx: <https://www.sphinx-doc.org/en/master/usage/extensions/graphviz.html>`_
|
||||
embeds graphs generated by the `Graphviz <https://graphviz.org/>`_
|
||||
module, which must be installed in order to render the
|
||||
inheritance diagrams in the :doc:`/reference`.
|
||||
- `Napoleon: <https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html>`_
|
||||
enables Sphinx to read Google style docstrings and, in
|
||||
particular for Manim, `NumPy style docstrings <https://numpydoc.readthedocs.io/en/latest/format.html>`_
|
||||
- see :doc:`docs/docstrings` for more information.
|
||||
|
||||
|
||||
Sphinx theme
|
||||
------------
|
||||
|
||||
The theme used for this website is `Furo <https://sphinx-themes.org/sample-sites/furo/>`_.
|
||||
|
||||
|
||||
Custom Sphinx directives
|
||||
------------------------
|
||||
|
||||
Manim implements **custom directives** for use with Autodoc and Autosummary, which
|
||||
are defined in :mod:`~.docbuild`:
|
||||
|
||||
.. currentmodule:: manim.utils.docbuild
|
||||
|
||||
.. autosummary::
|
||||
|
||||
:toctree: ../reference
|
||||
autoaliasattr_directive
|
||||
autocolor_directive
|
||||
manim_directive
|
||||
|
||||
|
||||
Index
|
||||
-----
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
docs/admonitions
|
||||
docs/docstrings
|
||||
docs/examples
|
||||
docs/references
|
||||
docs/typings
|
||||
docs/types
|
||||
|
|
@ -77,8 +77,7 @@ Example:
|
|||
The mobject linked to this instance.
|
||||
"""
|
||||
|
||||
def __init__(name: str, id: int, singleton: MyClass, mobj: Mobject = None):
|
||||
...
|
||||
def __init__(name: str, id: int, singleton: MyClass, mobj: Mobject = None): ...
|
||||
|
||||
2. The usage of ``Parameters`` on functions to specify how
|
||||
every parameter works and what it does. This should be excluded if
|
||||
147
docs/source/contributing/docs/types.rst
Normal file
147
docs/source/contributing/docs/types.rst
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
===================
|
||||
Choosing Type Hints
|
||||
===================
|
||||
In order to provide the best user experience,
|
||||
it's important that type hints are chosen correctly.
|
||||
With the large variety of types provided by Manim, choosing
|
||||
which one to use can be difficult. This guide aims to
|
||||
aid you in the process of choosing the right type for the scenario.
|
||||
|
||||
|
||||
The first step is figuring out which category your type hint fits into.
|
||||
|
||||
Coordinates
|
||||
-----------
|
||||
Coordinates encompass two main categories: points, and vectors.
|
||||
|
||||
|
||||
Points
|
||||
~~~~~~
|
||||
The purpose of points is pretty straightforward: they represent a point
|
||||
in space. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def print_point2D(coord: Point2DLike) -> None:
|
||||
x, y = coord
|
||||
print(f"Point at {x=},{y=}")
|
||||
|
||||
|
||||
def print_point3D(coord: Point3DLike) -> None:
|
||||
x, y, z = coord
|
||||
print(f"Point at {x=},{y=},{z=}")
|
||||
|
||||
|
||||
def print_point_array(coords: Point2DLike_Array | Point3DLike_Array) -> None:
|
||||
for coord in coords:
|
||||
if len(coord) == 2:
|
||||
# it's a Point2DLike
|
||||
print_point2D(coord)
|
||||
else:
|
||||
# it's a Point3DLike
|
||||
print_point3D(coord)
|
||||
|
||||
def shift_point_up(coord: Point3DLike) -> Point3D:
|
||||
result = np.asarray(coord)
|
||||
result += UP
|
||||
print(f"New point: {result}")
|
||||
return result
|
||||
|
||||
Notice that the last function, ``shift_point_up()``, accepts a
|
||||
:class:`~.Point3DLike` as a parameter and returns a :class:`~.Point3D`. A
|
||||
:class:`~.Point3D` always represents a NumPy array consisting of 3 floats,
|
||||
whereas a :class:`~.Point3DLike` can represent anything resembling a 3D point:
|
||||
either a NumPy array or a tuple/list of 3 floats, hence the ``Like`` word. The
|
||||
same happens with :class:`~.Point2D`, :class:`~.Point2D_Array` and
|
||||
:class:`~.Point3D_Array`, and their ``Like`` counterparts
|
||||
:class:`~.Point2DLike`, :class:`~.Point2DLike_Array` and
|
||||
:class:`~.Point3DLike_Array`.
|
||||
|
||||
The rule for typing functions is: **make parameter types as broad as possible,
|
||||
and return types as specific as possible.** Therefore, for functions which are
|
||||
intended to be called by users, **we should always, if possible, accept**
|
||||
``Like`` **types as parameters and return NumPy, non-** ``Like`` **types.** The
|
||||
main reason is to be more flexible with users who might want to pass tuples or
|
||||
lists as arguments rather than NumPy arrays, because it's more convenient. The
|
||||
last function, ``shift_point_up()``, is an example of it.
|
||||
|
||||
Internal functions which are *not* meant to be called by users may accept
|
||||
non-``Like`` parameters if necessary.
|
||||
|
||||
Vectors
|
||||
~~~~~~~
|
||||
Vectors share many similarities to points. However, they have a different
|
||||
connotation. Vectors should be used to represent direction. For example,
|
||||
consider this slightly contrived function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
M = TypeVar("M", bound=Mobject) # allow any mobject
|
||||
def shift_mobject(mob: M, direction: Vector3D, scale_factor: float = 1) -> M:
|
||||
return mob.shift(direction * scale_factor)
|
||||
|
||||
Here we see an important example of the difference. ``direction`` should not be
|
||||
typed as a :class:`~.Point3D`, because it represents a direction along
|
||||
which to shift a :class:`~.Mobject`, not a position in space.
|
||||
|
||||
As a general rule, if a parameter is called ``direction`` or ``axis``,
|
||||
it should be type hinted as some form of :class:`~.VectorND` or
|
||||
:class:`~.VectorNDLike`.
|
||||
|
||||
Colors
|
||||
------
|
||||
The interface Manim provides for working with colors is :class:`.ManimColor`.
|
||||
The main color types Manim supports are RGB, RGBA, and HSV. You will want
|
||||
to add type hints to a function depending on which type it uses. If any color will work,
|
||||
you will need something like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.utils.color import ParsableManimColor
|
||||
|
||||
# type hint stuff with ParsableManimColor
|
||||
|
||||
|
||||
|
||||
Béziers
|
||||
-------
|
||||
Manim internally represents a :class:`.Mobject` by a collection of points. In the case of :class:`.VMobject`,
|
||||
the most commonly used subclass of :class:`.Mobject`, these points represent Bézier curves,
|
||||
which are a way of representing a curve using a sequence of points.
|
||||
|
||||
.. note::
|
||||
|
||||
To learn more about Béziers, take a look at https://pomax.github.io/bezierinfo/
|
||||
|
||||
|
||||
Manim supports two different renderers, which each have different representations of
|
||||
Béziers: Cairo uses cubic Bézier curves, while OpenGL uses quadratic Bézier curves.
|
||||
|
||||
Type hints like :class:`~.typing.BezierPoints` represent a single bezier curve, and :class:`~.typing.BezierPath`
|
||||
represents multiple Bézier curves. A :class:`~.typing.Spline` is when the Bézier curves in a :class:`~.typing.BezierPath`
|
||||
forms a single connected curve. Manim also provides more specific type aliases when working with
|
||||
quadratic or cubic curves, and they are prefixed with their respective type (e.g. :class:`~.typing.CubicBezierPoints`,
|
||||
is a :class:`~.typing.BezierPoints` consisting of exactly 4 points representing a cubic Bézier curve).
|
||||
|
||||
|
||||
Functions
|
||||
---------
|
||||
Throughout the codebase, many different types of functions are used. The most obvious example
|
||||
is a rate function, which takes in a float and outputs a float (``Callable[[float], float]``).
|
||||
Another example is for overriding animations. One will often need to map a :class:`.Mobject`
|
||||
to an overridden :class:`.Animation`, and for that we have the :class:`~.typing.FunctionOverride` type hint.
|
||||
|
||||
:class:`~.typing.PathFuncType` and :class:`~.typing.MappingFunction` are more niche, but are related to moving objects
|
||||
along a path, or applying functions. If you need to use it, you'll know.
|
||||
|
||||
|
||||
Images
|
||||
------
|
||||
There are several representations of images in Manim. The most common is
|
||||
the representation as a NumPy array of floats representing the pixels of an image.
|
||||
This is especially common when it comes to the OpenGL renderer.
|
||||
|
||||
This is the use case of the :class:`~.typing.PixelArray` type hint. Sometimes, Manim may use ``PIL.Image.Image``,
|
||||
which is not the same as :class:`~.typing.PixelArray`. In this case, use the ``PIL.Image.Image`` typehint.
|
||||
Of course, if a more specific type of image is needed, it can be annotated as such.
|
||||
148
docs/source/contributing/docs/typings.rst
Normal file
148
docs/source/contributing/docs/typings.rst
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
==================
|
||||
Typing Conventions
|
||||
==================
|
||||
|
||||
.. warning::
|
||||
This section is still a work in progress.
|
||||
|
||||
Adding type hints to functions and parameters
|
||||
---------------------------------------------
|
||||
|
||||
Manim is currently in the process of adding type hints into the library. In this
|
||||
section, you will find information about the standards used and some general
|
||||
guidelines.
|
||||
|
||||
If you've never used type hints before, this is a good place to get started:
|
||||
https://realpython.com/python-type-checking/#hello-types.
|
||||
|
||||
Typing standards
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Manim uses `mypy`_ to type check its codebase. You will find a list of configuration values in the ``mypy.ini`` configuration file.
|
||||
To be able to use the newest typing features not available in the lowest
|
||||
supported Python version, make use of `typing_extensions`_.
|
||||
|
||||
To be able to use the new Union syntax (``|``) and builtins subscripting, use
|
||||
the ``from __future__ import annotations`` import.
|
||||
|
||||
.. _mypy: https://mypy-lang.org/
|
||||
.. _typing_extensions: https://pypi.org/project/typing-extensions/
|
||||
|
||||
Typing guidelines
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Manim has a dedicated :mod:`~.typing` module where type aliases are provided.
|
||||
Most of them may seem redundant, in particular the ones related to ``numpy``.
|
||||
This is in anticipation of the support for shape type hinting
|
||||
(`related issue <https://github.com/numpy/numpy/issues/16544>`_). Besides the
|
||||
pending shape support, using the correct type aliases will help users understand
|
||||
which shape should be used.
|
||||
|
||||
* For typings of generic collections, check out `this <https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes>`_
|
||||
link.
|
||||
|
||||
* Always use a type hint of ``None`` for functions that does not return
|
||||
a value (this also applies to ``__init__``), e.g.:
|
||||
|
||||
.. code:: py
|
||||
|
||||
def height(self, value) -> None:
|
||||
self.scale_to_fit_height(value)
|
||||
|
||||
* For variables representing paths, use the ``StrPath`` or ``StrOrBytesPath``
|
||||
type alias defined in the :mod:`~.typing` module.
|
||||
|
||||
* ``*args`` and ``**kwargs`` shouldn't be left untyped (in most cases you can
|
||||
use ``Any``).
|
||||
|
||||
* Following `PEP 484 <https://peps.python.org/pep-0484/#the-numeric-tower>`_,
|
||||
use ``float`` instead of ``int | float``.
|
||||
|
||||
* Use ``x | y`` instead of ``Union[x, y]``
|
||||
|
||||
* Mobjects have the typehint ``Mobject``, e.g.:
|
||||
|
||||
.. code:: py
|
||||
|
||||
def match_color(self, mobject: "Mobject"):
|
||||
"""Match the color with the color of another :class:`~.Mobject`."""
|
||||
return self.set_color(mobject.get_color())
|
||||
|
||||
* Always parametrize generics (``list[int]`` instead of ``list``,
|
||||
``type[Any]`` instead of ``type``, etc.). This also applies to callables.
|
||||
|
||||
.. code:: py
|
||||
|
||||
rate_func: Callable[[float], float] = lambda t: smooth(1 - t)
|
||||
|
||||
* Use ``TypeVar`` when you want to "link" type hints as being the same type.
|
||||
Consider ``Mobject.copy``, which returns a new instance of the same class.
|
||||
It would be type-hinted as:
|
||||
|
||||
.. code:: py
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def copy(self: T) -> T: ...
|
||||
|
||||
* Use ``typing.Iterable`` whenever the function works with *any* iterable, not a specific type.
|
||||
|
||||
* Prefer ``numpy.typing.NDArray`` over ``numpy.ndarray``
|
||||
|
||||
.. code:: py
|
||||
|
||||
import numpy as np
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy.typing as npt
|
||||
|
||||
|
||||
def foo() -> npt.NDArray[float]:
|
||||
return np.array([1, 0, 1])
|
||||
|
||||
* If a method returns ``self``, use ``typing_extensions.Self``.
|
||||
|
||||
.. code:: py
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
class CustomMobject:
|
||||
def set_color(self, color: ManimColor) -> Self:
|
||||
...
|
||||
return self
|
||||
|
||||
* If the function returns a container of a specific length each time, consider using ``tuple`` instead of ``list``.
|
||||
|
||||
.. code:: py
|
||||
|
||||
def foo() -> tuple[float, float, float]:
|
||||
return (0, 0, 0)
|
||||
|
||||
* If a function works with a parameter as long as said parameter has a ``__getitem__``, ``__iter___`` and ``__len__`` method,
|
||||
the typehint of the parameter should be ``collections.abc.Mapping``. If it also supports ``__setitem__`` and/or ``__delitem__``, it
|
||||
should be marked as ``collections.abc.MutableMapping``.
|
||||
|
||||
* Typehinting something as ``object`` means that only attributes available on every Python object should be accessed,
|
||||
like ``__str__`` and so on. On the other hand, literally any attribute can be accessed on a variable with the ``Any`` typehint -
|
||||
it's more freeing than the ``object`` typehint, and makes mypy stop typechecking the variable. Note that whenever possible,
|
||||
try to keep typehints as specific as possible.
|
||||
|
||||
* If objects are imported purely for type hint purposes, keep it under an ``if typing.TYPE_CHECKING`` guard, to prevent them from
|
||||
being imported at runtime (helps library performance). Do not forget to use the ``from __future__ import annotations`` import to avoid having runtime ``NameError`` exceptions.
|
||||
|
||||
.. code:: py
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.typing import Vector3D
|
||||
# type stuff with Vector3D
|
||||
|
||||
Missing Sections for typehints are:
|
||||
-----------------------------------
|
||||
|
||||
* Mypy and numpy import errors: https://realpython.com/python-type-checking/#running-mypy
|
||||
* Explain ``mypy.ini`` (see above link)
|
||||
|
|
@ -28,7 +28,7 @@ Contributing
|
|||
|
||||
That being said, improving the documentation and making it more accessible is still highly encouraged.
|
||||
And even if your work gets outdated and requires change, you or someone else can simply adjust the translation.
|
||||
Your efforts are not in vail!
|
||||
Your efforts are not in vain!
|
||||
|
||||
|
||||
Voting
|
||||
|
|
|
|||
|
|
@ -5,6 +5,24 @@ If you are adding new features to manim, you should add appropriate tests for th
|
|||
manim from breaking at each change by checking that no other
|
||||
feature has been broken and/or been unintentionally modified.
|
||||
|
||||
.. warning::
|
||||
|
||||
The full tests suite requires Cairo 1.18 in order to run all tests.
|
||||
However, Cairo 1.18 may not be available from your package manager,
|
||||
like ``apt``, and it is very likely that you have an older version installed,
|
||||
e.g., 1.16. If you run tests with a version prior to 1.18,
|
||||
many tests will be skipped. Those tests are not skipped in the online CI.
|
||||
|
||||
If you want to run all tests locally, you need to install Cairo 1.18 or above.
|
||||
You can do so by compiling Cairo from source:
|
||||
|
||||
1. download ``cairo-1.18.0.tar.xz`` from
|
||||
`here <https://www.cairographics.org/releases/>`_.
|
||||
and uncompress it;
|
||||
2. open the INSTALL file and follow the instructions (you might need to install
|
||||
``meson`` and ``ninja``);
|
||||
3. run the tests suite and verify that the Cairo version is correct.
|
||||
|
||||
How Manim tests
|
||||
---------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
==============
|
||||
Adding Typings
|
||||
==============
|
||||
|
||||
.. warning::
|
||||
This section is still a work in progress.
|
||||
|
||||
Adding type hints to functions and parameters
|
||||
---------------------------------------------
|
||||
|
||||
Manim is currently in the process of adding type hints into the library. In this
|
||||
section, you will find information about the standards used and some general
|
||||
guidelines.
|
||||
|
||||
If you've never used type hints before, this is a good place to get started:
|
||||
https://realpython.com/python-type-checking/#hello-types.
|
||||
|
||||
Typing standards
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Manim uses `mypy`_ to type check its codebase. You will find a list of
|
||||
configuration values in the ``mypy.ini`` configuration file.
|
||||
|
||||
To be able to use the newest typing features not available in the lowest
|
||||
supported Python version, make use of `typing_extensions`_.
|
||||
|
||||
To be able to use the new Union syntax (``|``) and builtins subscripting, use
|
||||
the ``from __future__ import annotations`` import.
|
||||
|
||||
.. _mypy: https://mypy-lang.org/
|
||||
.. _typing_extensions: https://pypi.org/project/typing-extensions/
|
||||
|
||||
Typing guidelines
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Manim has a dedicated :mod:`~.typing` module where type aliases are provided.
|
||||
Most of them may seem redundant, in particular the ones related to ``numpy``.
|
||||
This is in anticipation of the support for shape type hinting
|
||||
(`related issue <https://github.com/numpy/numpy/issues/16544>`_). Besides the
|
||||
pending shape support, using the correct type aliases will help users understand
|
||||
which shape should be used.
|
||||
|
||||
* Always use a type hint of ``None`` for functions that does not return
|
||||
a value (this also applies to ``__init__``), e.g.:
|
||||
|
||||
.. code:: py
|
||||
|
||||
def height(self, value) -> None:
|
||||
self.scale_to_fit_height(value)
|
||||
|
||||
* For variables representing paths, use the ``StrPath`` or ``StrOrBytesPath``
|
||||
type alias defined in the :mod:`~.typing` module.
|
||||
|
||||
* ``*args`` and ``**kwargs`` shouldn't be left untyped (in most cases you can
|
||||
use ``Any``).
|
||||
|
||||
* Following `PEP 484 <https://peps.python.org/pep-0484/#the-numeric-tower>`_,
|
||||
use ``float`` instead of ``int | float``.
|
||||
|
||||
* Mobjects have the typehint ``Mobject``, e.g.:
|
||||
|
||||
.. code:: py
|
||||
|
||||
def match_color(self, mobject: "Mobject"):
|
||||
"""Match the color with the color of another :class:`~.Mobject`."""
|
||||
return self.set_color(mobject.get_color())
|
||||
|
||||
* Always parametrize generics (``list[int]`` instead of ``list``,
|
||||
``type[Any]`` instead of ``type``, etc.). This also applies to callables:
|
||||
|
||||
.. code:: py
|
||||
|
||||
rate_func: Callable[[float], float] = lambda t: smooth(1 - t)
|
||||
|
||||
Missing Sections for typehints are:
|
||||
-----------------------------------
|
||||
|
||||
* Mypy and numpy import errors: https://realpython.com/python-type-checking/#running-mypy
|
||||
* When to use ``object`` vs ``Any``
|
||||
* The use of a TypeVar on the type hints for ``copy()``.
|
||||
* The definition and use of Protocols (like ``Sized``, ``Sequence``, ``Iterable``...)
|
||||
|
|
@ -90,11 +90,11 @@ Basic Concepts
|
|||
[[i * 256 / n for i in range(0, n)] for _ in range(0, n)]
|
||||
)
|
||||
image = ImageMobject(imageArray).scale(2)
|
||||
image.background_rectangle = SurroundingRectangle(image, GREEN)
|
||||
image.background_rectangle = SurroundingRectangle(image, color=GREEN)
|
||||
self.add(image, image.background_rectangle)
|
||||
|
||||
.. manim:: BooleanOperations
|
||||
:ref_classes: Union Intersection Exclusion
|
||||
:ref_classes: Union Intersection Exclusion Difference
|
||||
|
||||
class BooleanOperations(Scene):
|
||||
def construct(self):
|
||||
|
|
@ -299,7 +299,7 @@ Animations
|
|||
path.become(previous_path)
|
||||
path.add_updater(update_path)
|
||||
self.add(path, dot)
|
||||
self.play(Rotating(dot, radians=PI, about_point=RIGHT, run_time=2))
|
||||
self.play(Rotating(dot, angle=PI, about_point=RIGHT, run_time=2))
|
||||
self.wait()
|
||||
self.play(dot.animate.shift(UP))
|
||||
self.play(dot.animate.shift(LEFT))
|
||||
|
|
@ -341,7 +341,7 @@ Plotting with Manim
|
|||
axes.i2gp(TAU, cos_graph), color=YELLOW, line_func=Line
|
||||
)
|
||||
line_label = axes.get_graph_label(
|
||||
cos_graph, "x=2\pi", x_val=TAU, direction=UR, color=WHITE
|
||||
cos_graph, r"x=2\pi", x_val=TAU, direction=UR, color=WHITE
|
||||
)
|
||||
|
||||
plot = VGroup(axes, sin_graph, cos_graph, vert_line)
|
||||
|
|
@ -482,7 +482,7 @@ Plotting with Manim
|
|||
tips=False,
|
||||
)
|
||||
labels = ax.get_axis_labels(
|
||||
x_label=Tex("$\Delta Q$"), y_label=Tex("T[$^\circ C$]")
|
||||
x_label=Tex(r"$\Delta Q$"), y_label=Tex(r"T[$^\circ C$]")
|
||||
)
|
||||
|
||||
x_vals = [0, 8, 38, 39]
|
||||
|
|
@ -785,8 +785,8 @@ Advanced Projects
|
|||
|
||||
def add_x_labels(self):
|
||||
x_labels = [
|
||||
MathTex("\pi"), MathTex("2 \pi"),
|
||||
MathTex("3 \pi"), MathTex("4 \pi"),
|
||||
MathTex(r"\pi"), MathTex(r"2 \pi"),
|
||||
MathTex(r"3 \pi"), MathTex(r"4 \pi"),
|
||||
]
|
||||
|
||||
for i in range(len(x_labels)):
|
||||
|
|
|
|||
|
|
@ -120,19 +120,24 @@ of [ManimPango's README](https://github.com/ManimCommunity/ManimPango).
|
|||
|
||||
---
|
||||
|
||||
(not-on-path)=
|
||||
## I am using Windows and get the error `X is not recognized as an internal or external command, operable program or batch file`
|
||||
|
||||
Regardless of whether `X` says `python` or `manim`, this means that the executable you
|
||||
are trying to run is not located in one of the directories your system is looking
|
||||
for them (specified by the `PATH` variable). Take a look at the instructions
|
||||
{doc}`in the installation guide for Windows </installation/windows>`, or
|
||||
[this StackExchange answer](https://superuser.com/questions/143119/how-do-i-add-python-to-the-windows-path/143121#143121)
|
||||
to get help with editing the `PATH` variable manually.
|
||||
If you have followed {doc}`our local installation instructions </installation/uv>` and
|
||||
have not activated the corresponding virtual environment, make sure to use `uv run manim ...`
|
||||
instead of just `manim` (or activate the virtual environment by following the instructions
|
||||
printed when running `uv venv`).
|
||||
|
||||
If `python` is recognized but not `manim` or `pip`, you can try running
|
||||
Otherwise there is a problem with the directories where your system is looking for
|
||||
executables (the `PATH` variable).
|
||||
If `python` is recognized, you can try running
|
||||
commands by prepending `python -m`. That is, `manim` becomes `python -m manim`,
|
||||
and `pip` becomes `python -m pip`.
|
||||
|
||||
Otherwise see
|
||||
[this StackExchange answer](https://superuser.com/questions/143119/how-do-i-add-python-to-the-windows-path/143121#143121)
|
||||
to get help with editing the `PATH` variable manually.
|
||||
|
||||
---
|
||||
|
||||
## I have tried using Chocolatey (`choco install manimce`) to install Manim, but it failed!
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ or specific OpenGL classes like `OpenGLSurface`, but documentation for some of
|
|||
them is available in form of docstrings
|
||||
[in the source code](https://github.com/ManimCommunity/manim/tree/main/manim/mobject/opengl).
|
||||
|
||||
Furthermore, [this user guide by *aquabeam*](https://www.aquabeam.me/manim/opengl_guide/)
|
||||
Furthermore, [this user guide by *aquabeam*](https://web.archive.org/web/20250708135737/https://www.aquabeam.me/manim/opengl_guide/)
|
||||
can be helpful to get started using the OpenGL renderer.
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ and then record it during rendering:
|
|||
from manim_voiceover import VoiceoverScene
|
||||
from manim_voiceover.services.recorder import RecorderService
|
||||
|
||||
|
||||
# Simply inherit from VoiceoverScene instead of Scene to get all the
|
||||
# voiceover functionality.
|
||||
class RecorderExample(VoiceoverScene):
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ The scene then asks its renderer to initialize the scene by calling
|
|||
|
||||
Inspecting both the default Cairo renderer and the OpenGL renderer shows that the ``init_scene``
|
||||
method effectively makes the renderer instantiate a :class:`.SceneFileWriter` object, which
|
||||
basically is Manim's interface to ``ffmpeg`` and actually writes the movie file. The Cairo
|
||||
basically is Manim's interface to ``libav`` (FFMPEG) and actually writes the movie file. The Cairo
|
||||
renderer (see the implementation `here <https://github.com/ManimCommunity/manim/blob/main/manim/renderer/cairo_renderer.py>`__) does not require any further initialization. The OpenGL renderer
|
||||
does some additional setup to enable the realtime rendering preview window, which we do not go
|
||||
into detail further here.
|
||||
|
|
@ -310,8 +310,8 @@ the order they are called, these customizable methods are:
|
|||
After these three methods are run, the animations have been fully rendered,
|
||||
and Manim calls :meth:`.CairoRenderer.scene_finished` to gracefully
|
||||
complete the rendering process. This checks whether any animations have been
|
||||
played -- and if so, it tells the :class:`.SceneFileWriter` to close the pipe
|
||||
to ``ffmpeg``. If not, Manim assumes that a static image should be output
|
||||
played -- and if so, it tells the :class:`.SceneFileWriter` to close the output
|
||||
file. If not, Manim assumes that a static image should be output
|
||||
which it then renders using the same strategy by calling the render loop
|
||||
(see below) once.
|
||||
|
||||
|
|
@ -577,7 +577,7 @@ sound odd at first. Practically, this ensures that mobjects are not
|
|||
added twice, as mentioned above: if they were present in the scene
|
||||
``Scene.mobjects`` list before (even if they were contained as a
|
||||
child of some other mobject), they are first removed from the list.
|
||||
The way :meth:`.Scene.restrucutre_mobjects` works is rather aggressive:
|
||||
The way :meth:`.Scene.restructure_mobjects` works is rather aggressive:
|
||||
It always operates on a given list of mobjects; in the ``add`` method
|
||||
two different lists occur: the default one, ``Scene.mobjects`` (no extra
|
||||
keyword argument is passed), and ``Scene.moving_mobjects`` (which we will
|
||||
|
|
@ -762,10 +762,10 @@ to learn more, the :func:`.get_hash_from_play_call` function in the
|
|||
mechanism.
|
||||
|
||||
In the event that the animation has to be rendered, the renderer asks
|
||||
its :class:`.SceneFileWriter` to start a writing process. The process
|
||||
is started by a call to ``ffmpeg`` and opens a pipe to which rendered
|
||||
raw frames can be written. As long as the pipe is open, the process
|
||||
can be accessed via the ``writing_process`` attribute of the file writer.
|
||||
its :class:`.SceneFileWriter` to open an output container. The process
|
||||
is started by a call to ``libav`` and opens a container to which rendered
|
||||
raw frames can be written. As long as the output is open, the container
|
||||
can be accessed via the ``output_container`` attribute of the file writer.
|
||||
With the writing process in place, the renderer then asks the scene
|
||||
to "begin" the animations.
|
||||
|
||||
|
|
@ -815,7 +815,7 @@ time is extracted (3 seconds long) and stored in
|
|||
skip (it should not), then whether the animation is already
|
||||
cached (it is not). The corresponding animation hash value is
|
||||
determined and passed to the file writer, which then also calls
|
||||
``ffmpeg`` to start the writing process which waits for rendered
|
||||
``libav`` to start the writing process which waits for rendered
|
||||
frames from the library.
|
||||
|
||||
The scene then ``begin``\ s the animation: for the
|
||||
|
|
@ -1001,7 +1001,7 @@ and :meth:`.Animation.clean_up_from_scene` methods are called.
|
|||
In the end, the time progression is closed (which completes the displayed progress bar)
|
||||
in the terminal. With the closing of the time progression, the
|
||||
:meth:`.Scene.play_internal` call is completed, and we return to the renderer,
|
||||
which now orders the :class:`.SceneFileWriter` to close the movie pipe that has
|
||||
which now orders the :class:`.SceneFileWriter` to close the output container that has
|
||||
been opened for this animation: a partial movie file is written.
|
||||
|
||||
This pretty much concludes the walkthrough of a :class:`.Scene.play` call,
|
||||
|
|
|
|||
|
|
@ -2,17 +2,24 @@
|
|||
Rendering Text and Formulas
|
||||
###########################
|
||||
|
||||
There are two different ways by which you can render **Text** in videos:
|
||||
There are three different ways by which you can render **Text** in videos:
|
||||
|
||||
1. Using Pango (:mod:`~.text_mobject`)
|
||||
2. Using LaTeX (:mod:`~.tex_mobject`)
|
||||
3. Using Typst (:mod:`~.typst_mobject`)
|
||||
|
||||
If you want to render simple text, you should use either :class:`~.Text` or
|
||||
:class:`~.MarkupText`, or one of its derivatives like :class:`~.Paragraph`.
|
||||
Manim's Pango-based text classes include :class:`~.Text`,
|
||||
:class:`~.MarkupText`, and derivatives such as :class:`~.Paragraph`.
|
||||
See :ref:`using-text-objects` for more information.
|
||||
|
||||
LaTeX should be used when you need mathematical typesetting. See
|
||||
:ref:`rendering-with-latex` for more information.
|
||||
LaTeX rendering is available via :class:`~.Tex` and
|
||||
:class:`~.MathTex`. See :ref:`rendering-with-latex` for more
|
||||
information.
|
||||
|
||||
Typst support is available via :class:`~.Typst` and
|
||||
:class:`~.TypstMath`. It offers both general markup and mathematical
|
||||
typesetting through the Typst compiler without requiring a TeX
|
||||
distribution. See :ref:`typst-mobjects` for more information.
|
||||
|
||||
.. _using-text-objects:
|
||||
|
||||
|
|
@ -50,7 +57,7 @@ For example:
|
|||
)
|
||||
self.add(text)
|
||||
|
||||
.. _Pango library: https://pango.gnome.org
|
||||
.. _Pango library: https://pango.org
|
||||
|
||||
Working with :class:`~.Text`
|
||||
============================
|
||||
|
|
@ -291,6 +298,54 @@ and further references about PangoMarkup.
|
|||
)
|
||||
self.add(text)
|
||||
|
||||
.. _rendering-with-typst:
|
||||
|
||||
Text With Typst
|
||||
***************
|
||||
|
||||
Manim also supports rendering text and formulas with Typst via
|
||||
:class:`~.Typst` and :class:`~.TypstMath`.
|
||||
|
||||
.. important::
|
||||
|
||||
Typst support requires the optional ``typst`` dependency. Install it with
|
||||
``pip install manim[typst]``.
|
||||
|
||||
Typst mobjects compile Typst markup directly to SVG and import the result as
|
||||
vector graphics. This works both for general markup and for mathematical
|
||||
expressions.
|
||||
|
||||
.. manim:: HelloTypst
|
||||
:save_last_frame:
|
||||
:ref_classes: Typst
|
||||
|
||||
class HelloTypst(Scene):
|
||||
def construct(self):
|
||||
text = Typst(r"*Hello* from _Typst!_", font_size=96)
|
||||
self.add(text)
|
||||
|
||||
For mathematical expressions, use :class:`~.TypstMath`:
|
||||
|
||||
.. manim:: HelloTypstMath
|
||||
:save_last_frame:
|
||||
:ref_classes: TypstMath
|
||||
|
||||
class HelloTypstMath(Scene):
|
||||
def construct(self):
|
||||
equation = TypstMath(r"sum_(k=1)^n k = (n(n + 1)) / 2", font_size=72)
|
||||
self.add(equation)
|
||||
|
||||
Typst also supports selecting subexpressions via labels in the Typst source,
|
||||
or via Manim's ``{{ ... }}`` shorthand in :class:`~.TypstMath`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
eq = TypstMath("{{ a + b : lhs }} = {{ c }}")
|
||||
eq.select("lhs").set_color(BLUE)
|
||||
eq.select(0).set_color(YELLOW)
|
||||
|
||||
See :ref:`typst-mobjects` for more details and additional examples.
|
||||
|
||||
.. _rendering-with-latex:
|
||||
|
||||
Text With LaTeX
|
||||
|
|
@ -389,8 +444,9 @@ Substrings and parts
|
|||
|
||||
The TeX mobject can accept multiple strings as arguments. Afterwards you can
|
||||
refer to the individual parts either by their index (like ``tex[1]``), or by
|
||||
selecting parts of the tex code. In this example, we set the color
|
||||
of the ``\bigstar`` using :func:`~.set_color_by_tex`:
|
||||
using :func:`~.set_color_by_tex`, which matches the argument exactly against
|
||||
the strings passed to the constructor. In this example, we color the
|
||||
``\bigstar`` part:
|
||||
|
||||
.. manim:: LaTeXSubstrings
|
||||
:save_last_frame:
|
||||
|
|
@ -398,25 +454,13 @@ of the ``\bigstar`` using :func:`~.set_color_by_tex`:
|
|||
class LaTeXSubstrings(Scene):
|
||||
def construct(self):
|
||||
tex = Tex('Hello', r'$\bigstar$', r'\LaTeX', font_size=144)
|
||||
tex.set_color_by_tex('igsta', RED)
|
||||
tex.set_color_by_tex(r'$\bigstar$', RED)
|
||||
self.add(tex)
|
||||
|
||||
Note that :func:`~.set_color_by_tex` colors the entire substring containing
|
||||
the Tex, not just the specific symbol or Tex expression. Consider the following example:
|
||||
|
||||
.. manim:: IncorrectLaTeXSubstringColoring
|
||||
:save_last_frame:
|
||||
|
||||
class IncorrectLaTeXSubstringColoring(Scene):
|
||||
def construct(self):
|
||||
equation = MathTex(
|
||||
r"e^x = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots"
|
||||
)
|
||||
equation.set_color_by_tex("x", YELLOW)
|
||||
self.add(equation)
|
||||
|
||||
As you can see, this colors the entire equation yellow, contrary to what
|
||||
may be expected. To color only ``x`` yellow, we have to do the following:
|
||||
Because :func:`~.set_color_by_tex` requires an exact match, it cannot directly
|
||||
target a token inside a string that was passed as a single argument. To color
|
||||
every ``x`` in a formula, use ``substrings_to_isolate`` to split the string at
|
||||
each occurrence first:
|
||||
|
||||
.. manim:: CorrectLaTeXSubstringColoring
|
||||
:save_last_frame:
|
||||
|
|
@ -424,25 +468,33 @@ may be expected. To color only ``x`` yellow, we have to do the following:
|
|||
class CorrectLaTeXSubstringColoring(Scene):
|
||||
def construct(self):
|
||||
equation = MathTex(
|
||||
r"e^x = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots",
|
||||
r"e^{x} = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots",
|
||||
substrings_to_isolate="x"
|
||||
)
|
||||
equation.set_color_by_tex("x", YELLOW)
|
||||
self.add(equation)
|
||||
|
||||
By setting ``substrings_to_isolate`` to ``x``, we split up the
|
||||
:class:`~.MathTex` into substrings automatically and isolate the ``x`` components
|
||||
into individual substrings. Only then can :meth:`~.set_color_by_tex` be used
|
||||
to achieve the desired result.
|
||||
Each isolated occurrence of ``x`` becomes its own sub-mobject that
|
||||
:meth:`~.set_color_by_tex` can match exactly.
|
||||
If one of the ``substrings_to_isolate`` is in a sub or superscript, it needs
|
||||
to be enclosed by curly brackets.
|
||||
|
||||
Note that Manim also supports a custom syntax that allows splitting
|
||||
a TeX string into substrings easily: simply enclose parts of your formula
|
||||
that you want to isolate with double braces. In the string
|
||||
``MathTex(r"{{ a^2 }} + {{ b^2 }} = {{ c^2 }}")``, the rendered mobject
|
||||
``MathTex(r"{{ a^2 }} + {{ b^2 }} = {{ c^2 }}")``, the rendered mobject
|
||||
will consist of the substrings ``a^2``, ``+``, ``b^2``, ``=``, and ``c^2``.
|
||||
This makes transformations between similar text fragments easy
|
||||
to write using :class:`~.TransformMatchingTex`.
|
||||
|
||||
For Manim to recognise a ``{{`` as a group opener, it must appear either
|
||||
at the very start of the string or be immediately preceded by a whitespace
|
||||
character. This means that ``{{`` embedded directly after non-whitespace
|
||||
LaTeX — such as ``\frac{{{n}}}{k}`` or ``a^{{2}}`` — is left untouched,
|
||||
which prevents accidental splitting of ordinary nested-brace expressions.
|
||||
To stop a leading ``{{`` from being treated as a group opener, insert a
|
||||
space between the two braces: ``{{ ... }}`` → ``{ { ... } }``.
|
||||
|
||||
Using ``index_labels`` to work with complicated strings
|
||||
=======================================================
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,11 @@ in the right place!
|
|||
|
||||
.. note::
|
||||
|
||||
Please be aware that there are different, incompatible versions
|
||||
of Manim available. Check our :ref:`installation FAQ <different-versions>`
|
||||
Please be aware that there are different, incompatible versions of Manim available.
|
||||
This version, the Community Edition of Manim (`ManimCE <https://github.com/ManimCommunity/manim>`_),
|
||||
is a separate project maintained by the community, but it was forked from `3b1b/manim <https://github.com/3b1b/manim>`_,
|
||||
the original Manim created and open-sourced by Grant Sanderson, creator of `3Blue1Brown <https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw>`_ educational math videos.
|
||||
Check our :ref:`installation FAQ <different-versions>`
|
||||
to learn more!
|
||||
|
||||
- The :doc:`Installation <installation>` section has the latest and
|
||||
|
|
@ -30,7 +33,7 @@ in the right place!
|
|||
You can also find information on Manim's docker images and (online)
|
||||
notebook environments there.
|
||||
- Want to try the library before installing it? Take a look at our
|
||||
interactive online playground at https://try.manim.community in form
|
||||
interactive online playground at https://try.manim.community in the form
|
||||
of a Jupyter notebook.
|
||||
- In our :doc:`Tutorials <tutorials/index>` section you will find a
|
||||
collection of resources that will teach you how to use Manim. In particular,
|
||||
|
|
@ -71,7 +74,7 @@ Here are some short summaries for all of the sections in this documentation:
|
|||
can be found in the :doc:`FAQ </faq/index>` section.
|
||||
- The :doc:`Reference Manual </reference>` contains a comprehensive list of all of Manim's
|
||||
(documented) modules, classes, and functions. If you are somewhat familiar with Manim's
|
||||
module structure feel free to browse the manual directly. If you are searching for
|
||||
module structure, feel free to browse the manual directly. If you are searching for
|
||||
something specific, feel free to use the documentation's search feature in the sidebar.
|
||||
Many classes and methods come with their own illustrated examples too!
|
||||
- The :doc:`Plugins </plugins>` page documents how to install, write, and distribute
|
||||
|
|
@ -91,6 +94,21 @@ or `Discord <https://www.manim.community/discord/>`_. If you're using Manim in a
|
|||
context, instructions on how to cite a particular release can be found
|
||||
`in our README <https://github.com/ManimCommunity/manim/blob/main/README.md>`_.
|
||||
|
||||
License Information
|
||||
-------------------
|
||||
|
||||
Manim is an open-source library licensed under the **MIT License**, which applies to both the
|
||||
original and the community editions of the software. This means you are free to use, modify,
|
||||
and distribute the code in accordance with the MIT License terms. However, there are some
|
||||
additional points to be aware of:
|
||||
|
||||
- **Copyrighted Assets:** Specific assets, such as the "Pi creatures" in Grant Sanderson's
|
||||
(3Blue1Brown) videos, are copyrighted and protected. Please avoid using these characters in
|
||||
any derivative works.
|
||||
- **Content Creation and Sharing:** Videos and animations created with Manim can be freely
|
||||
shared, and no attribution to Manim is required—although it is much appreciated! You are
|
||||
encouraged to showcase your work online and share it with the Manim community.
|
||||
|
||||
Index
|
||||
-----
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ require no local installation. Head over to
|
|||
https://try.manim.community to give our interactive tutorial a try.
|
||||
|
||||
Otherwise, if you intend to use Manim to work on an animation project,
|
||||
we recommend installing the library locally (either to a conda environment,
|
||||
your system's Python, or via Docker).
|
||||
we recommend installing the library locally (preferably to some isolated
|
||||
virtual Python environment, or a conda-like environment, or via Docker).
|
||||
|
||||
.. warning::
|
||||
|
||||
|
|
@ -19,26 +19,52 @@ your system's Python, or via Docker).
|
|||
versions <different-versions>` if you are unsure which
|
||||
version you should install.
|
||||
|
||||
#. :ref:`(Recommended) Installing Manim via Python's package manager pip
|
||||
<local-installation>`
|
||||
#. :ref:`Installing Manim to a conda environment <conda-installation>`
|
||||
#. :ref:`Installing Manim to your system's Python <local-installation>`
|
||||
#. :ref:`Using Manim via Docker <docker-installation>`
|
||||
#. :ref:`Interactive Jupyter notebooks via Binder / Google Colab
|
||||
<interactive-online>`
|
||||
|
||||
|
||||
.. _local-installation:
|
||||
|
||||
Installing Manim locally via pip
|
||||
********************************
|
||||
|
||||
The recommended way of installing Manim is by using Python's package manager
|
||||
pip. If you already have a Python environment set up, you can simply run
|
||||
``pip install manim`` to install the library.
|
||||
|
||||
Our :doc:`local installation guide <installation/uv>` provides more detailed
|
||||
instructions, including best practices for setting up a suitable local environment.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
installation/uv
|
||||
|
||||
.. _conda-installation:
|
||||
|
||||
Installing Manim in conda
|
||||
*************************
|
||||
Installing Manim via Conda and related environment managers
|
||||
***********************************************************
|
||||
|
||||
Conda is a package manager for Python that allows creating environments
|
||||
where all your dependencies are stored. Like this, you don't clutter up your PC with
|
||||
unwanted libraries and you can just delete the environment when you don't need it anymore.
|
||||
It is a good way to install manim since all dependencies like
|
||||
``ffmpeg``, ``pycairo``, etc. come with it.
|
||||
It is a good way to install manim since all dependencies like ``pycairo``, etc. come with it.
|
||||
Also, the installation steps are the same, no matter if you are
|
||||
on Windows, Linux, Intel Macs or on Apple Silicon.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
There are various popular alternatives to Conda like
|
||||
`mamba <https://mamba.readthedocs.io/en/latest/>`__ /
|
||||
`micromamba <https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html>`__,
|
||||
or `pixi <https://pixi.sh>`__.
|
||||
They all can be used to setup a suitable, isolated environment
|
||||
for your Manim projects.
|
||||
|
||||
The following pages show how to install Manim in a conda environment:
|
||||
|
||||
.. toctree::
|
||||
|
|
@ -47,47 +73,6 @@ The following pages show how to install Manim in a conda environment:
|
|||
installation/conda
|
||||
|
||||
|
||||
|
||||
.. _local-installation:
|
||||
|
||||
Installing Manim locally
|
||||
************************
|
||||
|
||||
Manim is a Python library, and it can be
|
||||
`installed via pip <https://pypi.org/project/manim/>`__. However,
|
||||
in order for Manim to work properly, some additional system
|
||||
dependencies need to be installed first. The following pages have
|
||||
operating system specific instructions for you to follow.
|
||||
|
||||
Manim requires Python version ``3.8`` or above to run.
|
||||
|
||||
.. hint::
|
||||
|
||||
Depending on your particular setup, the installation process
|
||||
might be slightly different. Make sure that you have tried to
|
||||
follow the steps on the following pages carefully, but in case
|
||||
you hit a wall we are happy to help: either `join our Discord
|
||||
<https://www.manim.community/discord/>`__, or start a new
|
||||
Discussion `directly on GitHub
|
||||
<https://github.com/ManimCommunity/manim/discussions>`__.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation/windows
|
||||
installation/macos
|
||||
installation/linux
|
||||
|
||||
Once Manim is installed locally, you can proceed to our
|
||||
:doc:`quickstart guide <tutorials/quickstart>` which walks you
|
||||
through rendering a first simple scene.
|
||||
|
||||
As mentioned above, do not worry if there are errors or other
|
||||
problems: consult our :doc:`FAQ section </faq/index>` for help
|
||||
(including instructions for how to ask Manim's community for help).
|
||||
|
||||
|
||||
|
||||
.. _docker-installation:
|
||||
|
||||
Using Manim via Docker
|
||||
|
|
@ -131,6 +116,11 @@ If you're using Visual Studio Code you can install an extension called
|
|||
of the animation inside the editor. The extension can be installed through the
|
||||
`marketplace of VS Code <https://marketplace.visualstudio.com/items?itemName=Rickaym.manim-sideview>`__.
|
||||
|
||||
.. caution::
|
||||
|
||||
This extension is not officially maintained by the Manim Community.
|
||||
If you run into issues, please report them to the extension's author.
|
||||
|
||||
|
||||
Installation for developers
|
||||
***************************
|
||||
|
|
|
|||
|
|
@ -4,18 +4,31 @@ Conda
|
|||
Required Dependencies
|
||||
---------------------
|
||||
|
||||
To create a conda environment, you must first install
|
||||
`conda <https://docs.conda.io/projects/conda/en/latest/user-guide/install/download.html>`__
|
||||
or `mamba <https://mamba.readthedocs.io/en/latest/installation.html>`__,
|
||||
the two most popular conda clients.
|
||||
There are several package managers that work with conda packages,
|
||||
namely `conda <https://docs.conda.io/projects/conda/en/latest/user-guide/install/download.html>`__,
|
||||
`mamba <https://mamba.readthedocs.io>`__ and `pixi <https://pixi.sh>`__.
|
||||
|
||||
After installing conda, you can create a new environment and install ``manim`` inside by running
|
||||
After installing your package manager, you can create a new environment and install ``manim`` inside by running
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
conda create -n my-manim-environment
|
||||
conda activate my-manim-environment
|
||||
conda install -c conda-forge manim
|
||||
.. tab-set::
|
||||
|
||||
.. tab-item:: conda / mamba
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# if you want to use mamba, just replace conda below with mamba
|
||||
conda create -n my-manim-environment
|
||||
conda activate my-manim-environment
|
||||
conda install -c conda-forge manim
|
||||
|
||||
.. tab-item:: pixi
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pixi init
|
||||
pixi add manim
|
||||
|
||||
|
||||
Since all dependencies (except LaTeX) are handled by conda, you don't need to worry
|
||||
about needing to install additional dependencies.
|
||||
|
|
@ -29,11 +42,8 @@ In order to make use of Manim's interface to LaTeX to, for example, render
|
|||
equations, LaTeX has to be installed as well. Note that this is an optional
|
||||
dependency: if you don't intend to use LaTeX, you don't have to install it.
|
||||
|
||||
You can install LaTeX by following the optional dependencies steps
|
||||
for :ref:`Windows <win-optional-dependencies>`,
|
||||
:ref:`Linux <linux-optional-dependencies>` or
|
||||
:ref:`macOS <macos-optional-dependencies>`.
|
||||
|
||||
Recommendations on how to install LaTeX on different operating systems
|
||||
can be found :doc:`in our local installation guide </installation/uv>`.
|
||||
|
||||
|
||||
Working with Manim
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ For our image ``manimcommunity/manim``, there are the following tags:
|
|||
``-p`` (preview file) and ``-f`` (show output file in the file browser)
|
||||
are not supported.
|
||||
|
||||
.. note::
|
||||
|
||||
The Docker image ships with a minimal TeX Live installation. In particular,
|
||||
``ctex`` is not installed by default. If your scenes rely on
|
||||
``TexTemplateLibrary.ctex``, install it in the container via
|
||||
``tlmgr install ctex``.
|
||||
|
||||
|
||||
Basic usage of the Docker container
|
||||
-----------------------------------
|
||||
|
|
@ -45,7 +52,7 @@ modify to your liking. First, run
|
|||
|
||||
.. code-block:: sh
|
||||
|
||||
docker run -it --name my-manim-container -v "/full/path/to/your/directory:/manim" manimcommunity/manim /bin/bash
|
||||
docker run -it --name my-manim-container -v "/full/path/to/your/directory:/manim" manimcommunity/manim bash
|
||||
|
||||
|
||||
to obtain an interactive shell inside your container allowing you
|
||||
|
|
|
|||
|
|
@ -66,12 +66,12 @@ then execute it.
|
|||
.. code-block::
|
||||
|
||||
!sudo apt update
|
||||
!sudo apt install libcairo2-dev ffmpeg \
|
||||
!sudo apt install libcairo2-dev \
|
||||
texlive texlive-latex-extra texlive-fonts-extra \
|
||||
texlive-latex-recommended texlive-science \
|
||||
tipa libpango1.0-dev
|
||||
!pip install manim
|
||||
!pip install IPython --upgrade
|
||||
!pip install IPython==8.21.0
|
||||
|
||||
You should start to see Colab installing all the dependencies specified
|
||||
in these commands. After the execution has completed, you will be prompted
|
||||
|
|
|
|||
|
|
@ -1,171 +0,0 @@
|
|||
Linux
|
||||
=====
|
||||
|
||||
The installation instructions depend on your particular operating
|
||||
system and package manager. If you happen to know exactly what you are doing,
|
||||
you can also simply ensure that your system has:
|
||||
|
||||
- a reasonably recent version of Python 3 (3.8 or above),
|
||||
- with working Cairo bindings in the form of
|
||||
`pycairo <https://cairographics.org/pycairo/>`__,
|
||||
- FFmpeg accessible from the command line as ``ffmpeg``,
|
||||
- and `Pango <https://pango.gnome.org>`__ headers.
|
||||
|
||||
Then, installing Manim is just a matter of running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install manim
|
||||
|
||||
.. note::
|
||||
|
||||
In light of the current efforts of migrating to rendering via OpenGL,
|
||||
this list might be incomplete. Please `let us know
|
||||
<https://github.com/ManimCommunity/manim/issues/new/choose>` if you
|
||||
ran into missing dependencies while installing.
|
||||
|
||||
In any case, we have also compiled instructions for several common
|
||||
combinations of operating systems and package managers below.
|
||||
|
||||
Required Dependencies
|
||||
---------------------
|
||||
|
||||
apt – Ubuntu / Mint / Debian
|
||||
****************************
|
||||
|
||||
To first update your sources, and then install Cairo, Pango, and FFmpeg
|
||||
simply run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt update
|
||||
sudo apt install build-essential python3-dev libcairo2-dev libpango1.0-dev ffmpeg
|
||||
|
||||
If you don't have python3-pip installed, install it via:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt install python3-pip
|
||||
|
||||
Then, to install Manim, run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install manim
|
||||
|
||||
Continue by reading the :ref:`optional dependencies <linux-optional-dependencies>`
|
||||
section.
|
||||
|
||||
dnf – Fedora / CentOS / RHEL
|
||||
****************************
|
||||
|
||||
To install Cairo and Pango:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo dnf install cairo-devel pango-devel
|
||||
|
||||
In order to successfully build the ``pycairo`` wheel, you will also
|
||||
need the Python development headers:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo dnf install python3-devel
|
||||
|
||||
FFmpeg is only available via the RPMfusion repository which you have to
|
||||
configure first – please consult https://rpmfusion.org/Configuration/ for
|
||||
instructions. Then, install FFmpeg:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo dnf install ffmpeg
|
||||
|
||||
At this point you have all required dependencies and can install
|
||||
Manim by running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install manim
|
||||
|
||||
Continue by reading the :ref:`optional dependencies <linux-optional-dependencies>`
|
||||
section.
|
||||
|
||||
pacman – Arch / Manjaro
|
||||
***********************
|
||||
|
||||
.. tip::
|
||||
|
||||
Thanks to *groctel*, there is a `dedicated Manim package
|
||||
on the AUR! <https://aur.archlinux.org/packages/manim/>`
|
||||
|
||||
If you don't want to use the packaged version from AUR, here is what
|
||||
you need to do manually: Update your package sources, then install
|
||||
Cairo, Pango, and FFmpeg:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo pacman -Syu
|
||||
sudo pacman -S cairo pango ffmpeg
|
||||
|
||||
If you don't have ``python-pip`` installed, get it by running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo pacman -S python-pip
|
||||
|
||||
then simply install Manim via:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install manim
|
||||
|
||||
|
||||
Continue by reading the :ref:`optional dependencies <linux-optional-dependencies>`
|
||||
section.
|
||||
|
||||
|
||||
.. _linux-optional-dependencies:
|
||||
|
||||
Optional Dependencies
|
||||
---------------------
|
||||
|
||||
In order to make use of Manim's interface to LaTeX for, e.g., rendering
|
||||
equations, LaTeX has to be installed as well. Note that this is an optional
|
||||
dependency: if you don't intend to use LaTeX, you don't have to install it.
|
||||
|
||||
You can use whichever LaTeX distribution you like or whichever is easiest
|
||||
to install with your package manager. Usually,
|
||||
`TeX Live <https://www.tug.org/texlive/>`__ is a good candidate if you don't
|
||||
care too much about disk space.
|
||||
|
||||
For Debian-based systems (like Ubuntu), sufficient LaTeX dependencies can be
|
||||
installed by running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo apt install texlive texlive-latex-extra
|
||||
|
||||
For Fedora (see `docs <https://docs.fedoraproject.org/en-US/neurofedora/latex/>`__):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo dnf install texlive-scheme-full
|
||||
|
||||
Should you choose to work with some smaller TeX distribution like
|
||||
`TinyTeX <https://yihui.org/tinytex/>`__ , the full list
|
||||
of LaTeX packages which Manim interacts with in some way (a subset might
|
||||
be sufficient for your particular application) is::
|
||||
|
||||
collection-basic amsmath babel-english cbfonts-fd cm-super ctex doublestroke
|
||||
dvisvgm everysel fontspec frcursive fundus-calligra gnu-freefont jknapltx
|
||||
latex-bin mathastext microtype ms physics preview ragged2e relsize rsfs
|
||||
setspace standalone tipa wasy wasysym xcolor xetex xkeyval
|
||||
|
||||
|
||||
|
||||
Working with Manim
|
||||
------------------
|
||||
|
||||
At this point, you should have a working installation of Manim, head
|
||||
over to our :doc:`Quickstart Tutorial <../tutorials/quickstart>` to learn
|
||||
how to make your own *Manimations*!
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
macOS
|
||||
=====
|
||||
|
||||
For the sake of simplicity, the following instructions assume that you have
|
||||
the popular `package manager Homebrew <https://brew.sh>`__ installed. While
|
||||
you can certainly also install all dependencies without it, using Homebrew
|
||||
makes the process much easier.
|
||||
|
||||
If you want to use Homebrew but do not have it installed yet, please
|
||||
follow `Homebrew's installation instructions <https://docs.brew.sh/Installation>`__.
|
||||
|
||||
.. note::
|
||||
|
||||
For a while after Apple released its new ARM-based processors (the Apple Silicon chips like the *"M1 chip"*),
|
||||
the recommended way of installing Manim relied on *Rosetta*, Apple's compatibility
|
||||
layer between Intel and ARM architectures. This is no longer necessary, Manim can
|
||||
(and is recommended to) be installed natively.
|
||||
|
||||
|
||||
Required Dependencies
|
||||
---------------------
|
||||
|
||||
To install all required dependencies for installing Manim (namely: ffmpeg, Python,
|
||||
and some required Python packages), run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
brew install py3cairo ffmpeg
|
||||
|
||||
On *Apple Silicon* based machines (i.e., devices with the M1 chip or similar; if
|
||||
you are unsure which processor you have check by opening the Apple menu, select
|
||||
*About This Mac* and check the entry next to *Chip*), some additional dependencies
|
||||
are required, namely:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
brew install pango pkg-config scipy
|
||||
|
||||
After all required dependencies are installed, simply run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip3 install manim
|
||||
|
||||
to install Manim.
|
||||
|
||||
.. note::
|
||||
|
||||
A frequent source for installation problems is if ``pip3``
|
||||
does not point to the correct Python installation on your system.
|
||||
To check this, run ``pip3 -V``: for macOS Intel, the path should
|
||||
start with ``/usr/local``, and for Apple Silicon with
|
||||
``/opt/homebrew``. If this is not the case, you either forgot
|
||||
to modify your shell profile (``.zprofile``) during the installation
|
||||
of Homebrew, or did not reload your shell (e.g., by opening a new
|
||||
terminal) after doing so. It is also possible that some other
|
||||
software (like Pycharm) changed the ``PATH`` variable – to fix this,
|
||||
make sure that the Homebrew-related lines in your ``.zprofile`` are
|
||||
at the very end of the file.
|
||||
|
||||
.. _macos-optional-dependencies:
|
||||
|
||||
Optional Dependencies
|
||||
---------------------
|
||||
|
||||
In order to make use of Manim's interface to LaTeX for, e.g., rendering
|
||||
equations, LaTeX has to be installed as well. Note that this is an optional
|
||||
dependency: if you don't intend to use LaTeX, you don't have to install it.
|
||||
|
||||
For macOS, the recommended LaTeX distribution is
|
||||
`MacTeX <http://www.tug.org/mactex/>`__. You can install it by following
|
||||
the instructions from the link, or alternatively also via Homebrew by
|
||||
running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
brew install --cask mactex-no-gui
|
||||
|
||||
.. warning::
|
||||
|
||||
MacTeX is a *full* LaTeX distribution and will require more than 4GB of
|
||||
disk space. If this is an issue for you, consider installing a smaller
|
||||
distribution like
|
||||
`BasicTeX <http://www.tug.org/mactex/morepackages.html>`__.
|
||||
|
||||
Should you choose to work with some partial TeX distribution, the full list
|
||||
of LaTeX packages which Manim interacts with in some way (a subset might
|
||||
be sufficient for your particular application) is::
|
||||
|
||||
amsmath babel-english cbfonts-fd cm-super ctex doublestroke dvisvgm everysel
|
||||
fontspec frcursive fundus-calligra gnu-freefont jknapltx latex-bin
|
||||
mathastext microtype ms physics preview ragged2e relsize rsfs
|
||||
setspace standalone tipa wasy wasysym xcolor xetex xkeyval
|
||||
|
||||
|
||||
Working with Manim
|
||||
------------------
|
||||
|
||||
At this point, you should have a working installation of Manim. Head
|
||||
over to our :doc:`Quickstart Tutorial <../tutorials/quickstart>` to learn
|
||||
how to make your own *Manimations*!
|
||||
341
docs/source/installation/uv.md
Normal file
341
docs/source/installation/uv.md
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
# Installing Manim locally
|
||||
|
||||
The standard way of installing Manim is by using
|
||||
Python's package manager `pip` to install the latest
|
||||
release from [PyPI](https://pypi.org/project/manim/).
|
||||
|
||||
To make it easier for you to follow best practices when it
|
||||
comes to setting up a Python project for your Manim animations,
|
||||
we strongly recommend using a tool for managing Python environments
|
||||
and dependencies. In particular,
|
||||
[we strongly recommend using `uv`](https://docs.astral.sh/uv/#getting-started).
|
||||
|
||||
For the two main ways of installing Manim described below, we assume
|
||||
that `uv` is available; we think it is particularly helpful if you are
|
||||
new to Python or programming in general. It is not a hard requirement
|
||||
whatsoever; if you know what you are doing you can just use `pip` to
|
||||
install Manim directly.
|
||||
|
||||
:::::{admonition} Installing the Python management tool `uv`
|
||||
:class: seealso
|
||||
|
||||
One way to install `uv` is via the dedicated console installer supporting
|
||||
all large operating systems. Simply paste the following snippet into
|
||||
your terminal / PowerShell -- or
|
||||
[consult `uv`'s documentation](https://docs.astral.sh/uv/#getting-started)
|
||||
for alternative ways to install the tool.
|
||||
|
||||
::::{tab-set}
|
||||
:::{tab-item} MacOS and Linux
|
||||
```bash
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
```
|
||||
:::
|
||||
:::{tab-item} Windows
|
||||
```powershell
|
||||
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
```
|
||||
:::
|
||||
::::
|
||||
|
||||
:::::
|
||||
|
||||
Of course, if you know what you are doing and prefer to setup a virtual
|
||||
environment yourself, feel free to do so!
|
||||
|
||||
:::{important}
|
||||
If you run into issues when following our instructions below, do
|
||||
not worry: check our [installation FAQs](<project:/faq/installation.md>) to
|
||||
see whether the problem is already addressed there -- and otherwise go and
|
||||
check [how to contact our community](<project:/faq/help.md>) to get help.
|
||||
:::
|
||||
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Installing Python
|
||||
|
||||
We first need to check that an appropriate version of Python is available
|
||||
on your machine. Open a terminal to run
|
||||
```bash
|
||||
uv python install
|
||||
```
|
||||
to install the latest version of Python. If this is successful, continue
|
||||
to the next step.
|
||||
|
||||
(installation-optional-latex)=
|
||||
### Step 2 (optional): Installing LaTeX
|
||||
|
||||
[LaTeX](https://en.wikibooks.org/wiki/LaTeX/Mathematics) is a very well-known
|
||||
and widely used typesetting system allowing you to write formulas like
|
||||
|
||||
\begin{equation*}
|
||||
\frac{1}{2\pi i} \oint_{\gamma} \frac{f(z)}{(z - z_0)^{n+1}}~dz
|
||||
= \frac{f^{(n)}(z_0)}{n!}.
|
||||
\end{equation*}
|
||||
|
||||
If rendering plain text is sufficient for your needs and you don't want
|
||||
to render any typeset formulas, you can technically skip this step. Otherwise
|
||||
select your operating system from the tab list below and follow the instructions.
|
||||
|
||||
:::::{tab-set}
|
||||
|
||||
::::{tab-item} Windows
|
||||
For Windows we recommend installing LaTeX via the
|
||||
[MiKTeX distribution](https://miktex.org). Simply grab
|
||||
the Windows installer available from their download page,
|
||||
<https://miktex.org/download> and run it.
|
||||
::::
|
||||
|
||||
::::{tab-item} MacOS
|
||||
If you are running MacOS, we recommend installing the
|
||||
[MacTeX distribution](https://www.tug.org/mactex/). The latest
|
||||
available PKG file can be downloaded from
|
||||
<https://www.tug.org/mactex/mactex-download.html>.
|
||||
Get it and follow the standard installation procedure.
|
||||
::::
|
||||
|
||||
::::{tab-item} Linux
|
||||
Given the large number of Linux distributions with different ways
|
||||
of installing packages, we cannot give detailed instructions for
|
||||
all package managers.
|
||||
|
||||
In general we recommend to install a *TeX Live* distribution
|
||||
(<https://www.tug.org/texlive/>). For most Linux distributions,
|
||||
TeX Live has already been packaged such that it can be installed
|
||||
easily with your system package manager. Search the internet and
|
||||
your usual OS resources for detailed instructions.
|
||||
|
||||
For example, on Debian-based systems with the package manager `apt`,
|
||||
a full TeX Live distribution can be installed by running
|
||||
```bash
|
||||
sudo apt install texlive-full
|
||||
```
|
||||
For Fedora (managed via `dnf`), the corresponding command is
|
||||
```bash
|
||||
sudo dnf install texlive-scheme-full
|
||||
```
|
||||
As soon as LaTeX is installed, continue with actually installing Manim
|
||||
itself.
|
||||
|
||||
::::
|
||||
|
||||
:::::
|
||||
|
||||
:::{dropdown} I know what I am doing and I would like to setup a minimal LaTeX installation
|
||||
You are welcome to use a smaller, more customizable LaTeX distribution like
|
||||
[TinyTeX](https://yihui.org/tinytex/). Manim overall requires the following
|
||||
LaTeX packages to be installed in your distribution:
|
||||
```text
|
||||
amsmath babel-english cbfonts-fd cm-super count1to ctex doublestroke dvisvgm everysel
|
||||
fontspec frcursive fundus-calligra gnu-freefont jknapltx latex-bin
|
||||
mathastext microtype multitoc physics preview prelim2e ragged2e relsize rsfs
|
||||
setspace standalone tipa wasy wasysym xcolor xetex xkeyval
|
||||
```
|
||||
:::
|
||||
|
||||
### Step 3: Installing Manim
|
||||
|
||||
These steps again differ slightly between different operating systems. Make
|
||||
sure you select the correct one from the tab list below, then follow
|
||||
the instructions below.
|
||||
|
||||
::::::{tab-set}
|
||||
|
||||
:::::{tab-item} Windows
|
||||
The following commands will
|
||||
|
||||
- create a new directory for a Python project,
|
||||
- and add Manim as a dependency, which installs it into the corresponding
|
||||
local Python environment.
|
||||
|
||||
The name for the Python project is *manimations*, which you can change
|
||||
to anything you like.
|
||||
|
||||
```bash
|
||||
uv init manimations
|
||||
cd manimations
|
||||
uv add manim
|
||||
```
|
||||
|
||||
Manim is now installed in your local project environment!
|
||||
|
||||
:::::
|
||||
|
||||
:::::{tab-item} MacOS
|
||||
Before we can install Manim, we need to make sure that the system utilities
|
||||
`cairo` and `pkg-config` are present. They are needed for the [`pycairo` Python
|
||||
package](https://pycairo.readthedocs.io/en/latest/), a dependency of Manim.
|
||||
|
||||
The easiest way of installing these utilities is by using [Homebrew](https://brew.sh/),
|
||||
a fairly popular 3rd party package manager for MacOS. Check whether Homebrew is
|
||||
already installed by running
|
||||
|
||||
```bash
|
||||
brew --version
|
||||
```
|
||||
|
||||
which will report something along the lines of `Homebrew 4.4.15-54-...`
|
||||
if it is installed, and a message `command not found: brew` otherwise. In this
|
||||
case, use the shell installer [as instructed on Homebrew's website](https://brew.sh/),
|
||||
or get a `.pkg`-installer from
|
||||
[their GitHub release page](https://github.com/Homebrew/brew/releases). Make sure to
|
||||
follow the instructions of the installer carefully, especially when prompted to
|
||||
modify your `.zprofile` to add Homebrew to your system's PATH.
|
||||
|
||||
With Homebrew available, the required utilities can be installed by running
|
||||
|
||||
```bash
|
||||
brew install cairo pkg-config
|
||||
```
|
||||
|
||||
With all of this preparation out of the way, now it is time to actually install
|
||||
Manim itself! The following commands will
|
||||
|
||||
- create a new directory for a Python project,
|
||||
- and add Manim as a dependency, which installs it into the corresponding
|
||||
local Python environment.
|
||||
|
||||
The name for the Python project is *manimations*, which you can change
|
||||
to anything you like.
|
||||
|
||||
```bash
|
||||
uv init manimations
|
||||
cd manimations
|
||||
uv add manim
|
||||
```
|
||||
|
||||
Manim is now installed in your local project environment!
|
||||
|
||||
:::::
|
||||
|
||||
:::::{tab-item} Linux
|
||||
Practically, the instructions given in the *Windows* tab
|
||||
also apply for Linux -- however, some additional dependencies are
|
||||
required as Linux users need to build
|
||||
[ManimPango](https://github.com/ManimCommunity/ManimPango)
|
||||
(and potentially [pycairo](https://pycairo.readthedocs.io/en/latest/))
|
||||
from source. More specifically, this includes:
|
||||
|
||||
- A C compiler,
|
||||
- Python's development headers,
|
||||
- the `pkg-config` tool,
|
||||
- Pango and its development headers,
|
||||
- and Cairo and its development headers.
|
||||
|
||||
Instructions for popular systems / package managers are given below.
|
||||
|
||||
::::{tab-set}
|
||||
|
||||
:::{tab-item} Debian-based / apt
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install build-essential python3-dev libcairo2-dev libpango1.0-dev
|
||||
```
|
||||
:::
|
||||
|
||||
:::{tab-item} Fedora / dnf
|
||||
```bash
|
||||
sudo dnf install python3-devel pkg-config cairo-devel pango-devel
|
||||
```
|
||||
:::
|
||||
|
||||
:::{tab-item} Arch Linux / pacman
|
||||
```bash
|
||||
sudo pacman -Syu base-devel cairo pango
|
||||
```
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
As soon as the required dependencies are installed, you can create
|
||||
a Python project (feel free to change the name *manimations* used below
|
||||
to some other name) with a local environment containing Manim by running
|
||||
```bash
|
||||
uv init manimations
|
||||
cd manimations
|
||||
uv add manim
|
||||
```
|
||||
|
||||
:::::
|
||||
|
||||
::::::
|
||||
|
||||
To verify that your local Python project is setup correctly
|
||||
and that Manim is available, simply run
|
||||
```bash
|
||||
uv run manim checkhealth
|
||||
```
|
||||
|
||||
At this point, you can also open your project folder with the
|
||||
IDE of your choice. All modern Python IDEs (for example VS Code
|
||||
with the Python extension, or PyCharm) should automatically detect
|
||||
the local environment created by `uv` such that if you put
|
||||
```py
|
||||
import manim
|
||||
```
|
||||
into a new file `my-first-animation.py`, the import is resolved
|
||||
correctly and autocompletion is available.
|
||||
|
||||
*Happy Manimating!*
|
||||
|
||||
|
||||
:::{dropdown} Alternative: Installing Manim as a global `uv`-managed tool
|
||||
If you have Manim projects in many different directories and you do not
|
||||
want to setup a local project environment for each of them, you could
|
||||
also install Manim as a `uv`-managed tool.
|
||||
|
||||
See [`uv`'s documentation for more information](https://docs.astral.sh/uv/concepts/tools/)
|
||||
on their tool mechanism.
|
||||
|
||||
To install Manim as a global `uv` tool, simply run
|
||||
```bash
|
||||
uv tool install manim
|
||||
```
|
||||
after which the `manim` executable will be available on your
|
||||
global system path, without the need to activate any virtual
|
||||
environment or prefixing your commands with `uv run`.
|
||||
|
||||
Note that when using this approach, setting up your code editor
|
||||
to properly resolve `import manim` requires additional work, as
|
||||
the global tool environment is not automatically detected: the
|
||||
base path of all tool environments can be determined by running
|
||||
```
|
||||
uv tool dir
|
||||
```
|
||||
which should now contain a directory `manim` in which the appropriate
|
||||
virtual environment is located. Set the Python interpreter of your IDE
|
||||
to this environment to make imports properly resolve themselves.
|
||||
:::
|
||||
|
||||
:::{dropdown} Installing Manim for a different version of Python
|
||||
In case you would like to use a different version of Python
|
||||
(for example, due to compatibility issues with other packages),
|
||||
then `uv` allows you to do so in a fairly straightforward way.
|
||||
|
||||
When initializing the local Python project, simply pass the Python
|
||||
version you want to use as an argument to the `init` command:
|
||||
```
|
||||
uv init --python 3.12 manimations
|
||||
cd manimations
|
||||
uv add manim
|
||||
```
|
||||
To change the version for an existing package, you will need to
|
||||
edit the `pyproject.toml` file. If you are downgrading the python version, the
|
||||
`requires-python` entry needs to be updated such that your chosen
|
||||
version satisfies the requirement. Change the line to, for example
|
||||
`requires-python = ">=3.12"`. After that, run `uv python pin 3.12`
|
||||
to pin the python version to `3.12`. Finally, run `uv sync`, and your
|
||||
environment is updated!
|
||||
:::
|
||||
|
||||
:::{dropdown} Installing the latest development version
|
||||
If you want to install the latest (potentially unstable!)
|
||||
development version of Manim from our source repository
|
||||
[on GitHub](https://github.com/ManimCommunity/manim), then
|
||||
simply run
|
||||
```bash
|
||||
uv add git+https://github.com/ManimCommunity/manim.git@main
|
||||
```
|
||||
:::
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
Windows
|
||||
=======
|
||||
|
||||
The easiest way of installing Manim and its dependencies is by using a
|
||||
package manager like `Chocolatey <https://chocolatey.org/>`__
|
||||
or `Scoop <https://scoop.sh>`__. If you are not afraid of editing
|
||||
your System's ``PATH``, a manual installation is also possible.
|
||||
In fact, if you already have an existing Python
|
||||
installation (3.8 or above), it might be the easiest way to get
|
||||
everything up and running.
|
||||
|
||||
If you choose to use one of the package managers, please follow
|
||||
their installation instructions
|
||||
(`for Chocolatey <https://chocolatey.org/install#install-step2>`__,
|
||||
`for Scoop <https://scoop-docs.now.sh/docs/getting-started/Quick-Start.html>`__)
|
||||
to make one of them available on your system.
|
||||
|
||||
|
||||
Required Dependencies
|
||||
---------------------
|
||||
|
||||
Manim requires a recent version of Python (3.8 or above) and ``ffmpeg``
|
||||
in order to work.
|
||||
|
||||
Chocolatey
|
||||
**********
|
||||
|
||||
Manim can be installed via Chocolatey simply by running:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
choco install manimce
|
||||
|
||||
That's it, no further steps required. You can continue with installing
|
||||
the :ref:`optional dependencies <win-optional-dependencies>` below.
|
||||
|
||||
Scoop
|
||||
*****
|
||||
|
||||
While there is no recipe for installing Manim with Scoop directly,
|
||||
you can install all requirements by running:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
scoop install python ffmpeg
|
||||
|
||||
and then Manim can be installed by running:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
python -m pip install manim
|
||||
|
||||
Manim should now be installed on your system. Continue reading
|
||||
the :ref:`optional dependencies <win-optional-dependencies>` section
|
||||
below.
|
||||
|
||||
Winget
|
||||
******
|
||||
|
||||
While there is no recipe for installing Manim with Winget directly,
|
||||
you can install all requirements by running:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
winget install python
|
||||
winget install ffmpeg
|
||||
|
||||
and then Manim can be installed by running:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
python -m pip install manim
|
||||
|
||||
Manim should now be installed on your system. Continue reading
|
||||
the :ref:`optional dependencies <win-optional-dependencies>` section
|
||||
below.
|
||||
|
||||
|
||||
Manual Installation
|
||||
*******************
|
||||
|
||||
As mentioned above, Manim needs a reasonably recent version of
|
||||
Python 3 (3.8 or above) and FFmpeg.
|
||||
|
||||
**Python:** Head over to https://www.python.org, download an installer
|
||||
for a recent version of Python, and follow its instructions to get Python
|
||||
installed on your system.
|
||||
|
||||
.. note::
|
||||
|
||||
We have received reports of problems caused by using the version of
|
||||
Python that can be installed from the Windows Store. At this point,
|
||||
we recommend staying away from the Windows Store version. Instead,
|
||||
install Python directly from the
|
||||
`official website <https://www.python.org>`__.
|
||||
|
||||
**FFmpeg:** In order to install FFmpeg, you can get a
|
||||
pre-compiled and ready-to-use version from one of the resources
|
||||
linked at https://ffmpeg.org/download.html#build-windows, such as
|
||||
`the version available here
|
||||
<https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.7z>`__
|
||||
(recommended), or if you know exactly what you are doing
|
||||
you can alternatively get the source code
|
||||
from https://ffmpeg.org/download.html and compile it yourself.
|
||||
|
||||
|
||||
After downloading the pre-compiled archive,
|
||||
`unzip it <https://www.7-zip.org>`__ and, if you like, move the
|
||||
extracted directory to some more permanent place (e.g.,
|
||||
``C:\Program Files\``). Next, edit the ``PATH`` environment variable:
|
||||
first, visit ``Control Panel`` > ``System`` > ``System settings`` >
|
||||
``Environment Variables``, then add the full path to the ``bin``
|
||||
directory inside of the (moved) ffmpeg directory to the
|
||||
``PATH`` variable. Finally, save your changes and exit.
|
||||
|
||||
If you now open a new command line prompt (or PowerShell) and
|
||||
run ``ffmpeg``, the command should be recognized.
|
||||
|
||||
At this point, you have all the required dependencies and can now
|
||||
install Manim via
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
python -m pip install manim
|
||||
|
||||
|
||||
.. _win-optional-dependencies:
|
||||
|
||||
Optional Dependencies
|
||||
---------------------
|
||||
|
||||
In order to make use of Manim's interface to LaTeX to, for example, render
|
||||
equations, LaTeX has to be installed as well. Note that this is an optional
|
||||
dependency: if you don't intend to use LaTeX, you don't have to install it.
|
||||
|
||||
For Windows, the recommended LaTeX distribution is
|
||||
`MiKTeX <https://miktex.org/download>`__. You can install it by using the
|
||||
installer from the linked MiKTeX site, or by using the package manager
|
||||
of your choice (Chocolatey: ``choco install miktex.install``,
|
||||
Scoop: ``scoop install latex``, Winget: ``winget install MiKTeX.MiKTeX``).
|
||||
|
||||
If you are concerned about disk space, there are some alternative,
|
||||
smaller distributions of LaTeX.
|
||||
|
||||
**Using Chocolatey:** If you used Chocolatey to install manim or are already
|
||||
a chocolatey user, then you can simply run ``choco install manim-latex``. It
|
||||
is a dedicated package for Manim based on TinyTeX which contains all the
|
||||
required packages that Manim interacts with.
|
||||
|
||||
**Manual Installation:**
|
||||
You can also use `TinyTeX <https://yihui.org/tinytex/>`__ (Chocolatey: ``choco install tinytex``,
|
||||
Scoop: first ``scoop bucket add r-bucket https://github.com/cderv/r-bucket.git``,
|
||||
then ``scoop install tinytex``) alternative installation instructions can be found at their website.
|
||||
Keep in mind that you will have to manage the LaTeX packages installed on your system yourself via ``tlmgr``.
|
||||
Therefore we only recommend this option if you know what you are doing.
|
||||
|
||||
The full list of LaTeX packages which Manim interacts with in some way
|
||||
(a subset might be sufficient for your particular application) are::
|
||||
|
||||
amsmath babel-english cbfonts-fd cm-super ctex doublestroke dvisvgm everysel
|
||||
fontspec frcursive fundus-calligra gnu-freefont jknapltx latex-bin
|
||||
mathastext microtype ms physics preview ragged2e relsize rsfs
|
||||
setspace standalone tipa wasy wasysym xcolor xetex xkeyval
|
||||
|
||||
|
||||
|
||||
Working with Manim
|
||||
------------------
|
||||
|
||||
At this point, you should have a working installation of Manim, head
|
||||
over to our :doc:`Quickstart Tutorial <../tutorials/quickstart>` to learn
|
||||
how to make your own *Manimations*!
|
||||
|
|
@ -96,43 +96,22 @@ The only requirement of manim plugins is that they specify an entry point
|
|||
with the group, ``"manim.plugins"``. This allows Manim to discover plugins
|
||||
available in the user's environment. Everything regarding the plugin's
|
||||
directory structure, build system, and naming are completely up to your
|
||||
discretion as an author. The aforementioned template plugin is only a model
|
||||
using Poetry since this is the build system Manim uses. The plugin's `entry
|
||||
point <https://packaging.python.org/specifications/entry-points/>`_ can be
|
||||
specified in poetry as:
|
||||
discretion as an author.
|
||||
|
||||
The standard way to specify an entry point (see
|
||||
`the Python packaging guide <https://packaging.python.org/specifications/entry-points/>`__
|
||||
for details) is to include the following in your ``pyproject.toml``:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[tool.poetry.plugins."manim.plugins"]
|
||||
[project.entry-points."manim.plugins"]
|
||||
"name" = "object_reference"
|
||||
|
||||
Here ``name`` is the name of the module of the plugin.
|
||||
.. versionremoved:: 0.18.1
|
||||
|
||||
Here ``object_reference`` can point to either a function in a module or a module
|
||||
itself. For example,
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[tool.poetry.plugins."manim.plugins"]
|
||||
"manim_plugintemplate" = "manim_plugintemplate"
|
||||
|
||||
Here a module is used as ``object_reference``, and when this plugin is enabled,
|
||||
Manim will look for ``__all__`` keyword defined in ``manim_plugintemplate`` and
|
||||
everything as a global variable one by one.
|
||||
|
||||
If ``object_reference`` is a function, Manim calls the function and expects the
|
||||
function to return a list of modules or functions that need to be defined globally.
|
||||
|
||||
For example,
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[tool.poetry.plugins."manim.plugins"]
|
||||
"manim_plugintemplate" = "manim_awesomeplugin.imports:setup_things"
|
||||
|
||||
Here, Manim will call the function ``setup_things`` defined in
|
||||
``manim_awesomeplugin.imports`` and calls that. It returns a list of function or
|
||||
modules which will be imported globally.
|
||||
Plugins should be imported explicitly to be usable in user code. The plugin
|
||||
system will probably be refactored in the future to provide a more structured
|
||||
interface.
|
||||
|
||||
A note on Renderer Compatibility
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -9,23 +9,27 @@ Module Index
|
|||
.. autosummary::
|
||||
:toctree: ../reference
|
||||
|
||||
constants
|
||||
~utils.bezier
|
||||
cli
|
||||
~utils.color
|
||||
~utils.commands
|
||||
~utils.config_ops
|
||||
~utils.deprecation
|
||||
constants
|
||||
data_structures
|
||||
~utils.debug
|
||||
~utils.deprecation
|
||||
~utils.docbuild
|
||||
~utils.hashing
|
||||
~utils.ipython_magic
|
||||
~utils.images
|
||||
~utils.ipython_magic
|
||||
~utils.iterables
|
||||
~utils.paths
|
||||
~utils.rate_functions
|
||||
~utils.simple_functions
|
||||
~utils.sounds
|
||||
~utils.space_ops
|
||||
~utils.testing
|
||||
~utils.tex
|
||||
~utils.tex_templates
|
||||
~utils.tex_file_writing
|
||||
~utils.tex_templates
|
||||
typing
|
||||
|
|
|
|||
|
|
@ -327,6 +327,13 @@ Generally, you start with the starting number and add only some part of the valu
|
|||
So, the logic of calculating the number to display at each step will be ``50 + alpha * (100 - 50)``.
|
||||
Once you set the calculated value for the :class:`~.DecimalNumber`, you are done.
|
||||
|
||||
.. note::
|
||||
|
||||
If you're creating a custom animation and want to use a ``rate_func``, you must explicitly apply
|
||||
``self.rate_func(alpha)`` to the parameter you're animating. For example, try switching the rate
|
||||
function to ``rate_functions.there_and_back`` to observe how it affects the counting behavior.
|
||||
|
||||
|
||||
Once you have defined your ``Count`` animation, you can play it in your :class:`~.Scene` for any duration you want for any :class:`~.DecimalNumber` with any rate function.
|
||||
|
||||
.. manim:: CountingScene
|
||||
|
|
@ -343,7 +350,7 @@ Once you have defined your ``Count`` animation, you can play it in your :class:`
|
|||
|
||||
def interpolate_mobject(self, alpha: float) -> None:
|
||||
# Set value of DecimalNumber according to alpha
|
||||
value = self.start + (alpha * (self.end - self.start))
|
||||
value = self.start + (self.rate_func(alpha) * (self.end - self.start))
|
||||
self.mobject.set_value(value)
|
||||
|
||||
|
||||
|
|
@ -376,13 +383,13 @@ and :meth:`~.Mobject.get_start`. Here is an example of some important coordinate
|
|||
|
||||
class MobjectExample(Scene):
|
||||
def construct(self):
|
||||
p1= [-1,-1,0]
|
||||
p2= [1,-1,0]
|
||||
p3= [1,1,0]
|
||||
p4= [-1,1,0]
|
||||
a = Line(p1,p2).append_points(Line(p2,p3).points).append_points(Line(p3,p4).points)
|
||||
point_start= a.get_start()
|
||||
point_end = a.get_end()
|
||||
p1 = [-1,-1, 0]
|
||||
p2 = [ 1,-1, 0]
|
||||
p3 = [ 1, 1, 0]
|
||||
p4 = [-1, 1, 0]
|
||||
a = Line(p1,p2).append_points(Line(p2,p3).points).append_points(Line(p3,p4).points)
|
||||
point_start = a.get_start()
|
||||
point_end = a.get_end()
|
||||
point_center = a.get_center()
|
||||
self.add(Text(f"a.get_start() = {np.round(point_start,2).tolist()}", font_size=24).to_edge(UR).set_color(YELLOW))
|
||||
self.add(Text(f"a.get_end() = {np.round(point_end,2).tolist()}", font_size=24).next_to(self.mobjects[-1],DOWN).set_color(RED))
|
||||
|
|
@ -425,8 +432,8 @@ function of numpy:
|
|||
self.camera.background_color = WHITE
|
||||
m1a = Square().set_color(RED).shift(LEFT)
|
||||
m1b = Circle().set_color(RED).shift(LEFT)
|
||||
m2a= Square().set_color(BLUE).shift(RIGHT)
|
||||
m2b= Circle().set_color(BLUE).shift(RIGHT)
|
||||
m2a = Square().set_color(BLUE).shift(RIGHT)
|
||||
m2b = Circle().set_color(BLUE).shift(RIGHT)
|
||||
|
||||
points = m2a.points
|
||||
points = np.roll(points, int(len(points)/4), axis=0)
|
||||
|
|
|
|||
|
|
@ -276,25 +276,28 @@ When executing the command
|
|||
|
||||
manim -pql scene.py SquareToCircle
|
||||
|
||||
it was necessary to specify which ``Scene`` class to render. This is because a
|
||||
single file can contain more than one ``Scene`` class. If your file contains
|
||||
multiple ``Scene`` classes, and you want to render them all, you can use the
|
||||
``-a`` flag.
|
||||
it specifies the scene to render. This is not necessary now. When a single
|
||||
file contains only one ``Scene`` class, it will just render the ``Scene``
|
||||
class. When a single file contains more than one ``Scene`` class, manim will
|
||||
let you choose a ``Scene`` class. If your file contains multiple ``Scene``
|
||||
classes, and you want to render them all, you can use the ``-a`` flag.
|
||||
|
||||
As discussed previously, the ``-ql`` specifies low render quality. This does
|
||||
not look very good, but is very useful for rapid prototyping and testing. The
|
||||
other options that specify render quality are ``-qm``, ``-qh``, and ``-qk`` for
|
||||
medium, high, and 4k quality, respectively.
|
||||
As discussed previously, the ``-ql`` specifies low render quality (854x480
|
||||
15FPS). This does not look very good, but is very useful for rapid
|
||||
prototyping and testing. The other options that specify render quality are
|
||||
``-qm``, ``-qh``, ``-qp`` and ``-qk`` for medium (1280x720 30FPS), high
|
||||
(1920x1080 60FPS), 2k (2560x1440 60FPS) and 4k quality (3840x2160 60FPS),
|
||||
respectively.
|
||||
|
||||
The ``-p`` flag plays the animation once it is rendered. If you want to open
|
||||
the file browser at the location of the animation instead of playing it, you
|
||||
can use the ``-f`` flag. You can also omit these two flags.
|
||||
|
||||
Finally, by default manim will output .mp4 files. If you want your animations
|
||||
in .gif format instead, use the ``-i`` flag. The output files will be in the
|
||||
same folder as the .mp4 files, and with the same name, but a different file
|
||||
extension.
|
||||
in .gif format instead, use the ``--format gif`` flag. The output files will
|
||||
be in the same folder as the .mp4 files, and with the same name, but a
|
||||
different file extension.
|
||||
|
||||
This was a quick review of some of the most frequent command-line flags. For a
|
||||
thorough review of all flags available, see the
|
||||
:doc:`thematic guide on Manim's configuration system </guides/configuration>`.
|
||||
This was a quick review of some of the most frequent command-line flags.
|
||||
For a thorough review of all flags available, see the :doc:`thematic guide on
|
||||
Manim's configuration system </guides/configuration>`.
|
||||
|
|
|
|||
|
|
@ -3,12 +3,22 @@ Quickstart
|
|||
==========
|
||||
|
||||
.. note::
|
||||
Before proceeding, install Manim and make sure it's running properly by
|
||||
following the steps in :doc:`../installation`. For
|
||||
information on using Manim with Jupyterlab or Jupyter notebook, go to the
|
||||
documentation for the
|
||||
:meth:`IPython magic command <manim.utils.ipython_magic.ManimMagic.manim>`,
|
||||
``%%manim``.
|
||||
|
||||
Before proceeding, install Manim and make sure it is running properly by
|
||||
following the steps in :doc:`../installation`. For
|
||||
information on using Manim with Jupyterlab or Jupyter notebook, go to the
|
||||
documentation for the
|
||||
:meth:`IPython magic command <manim.utils.ipython_magic.ManimMagic.manim>`,
|
||||
``%%manim``.
|
||||
|
||||
|
||||
.. important::
|
||||
|
||||
If you installed Manim in the recommended way, using the
|
||||
Python management tool ``uv``, then you either need to make sure the corresponding
|
||||
virtual environment is activated (follow the instructions printed on running ``uv venv``),
|
||||
or you need to remember to prefix the ``manim`` command in the console with ``uv run``;
|
||||
that is, ``uv run manim ...``.
|
||||
|
||||
Overview
|
||||
********
|
||||
|
|
@ -28,45 +38,38 @@ use to modify ``Mobject``\s.
|
|||
Starting a new project
|
||||
**********************
|
||||
|
||||
Start by creating a new folder. For the purposes of this guide, name the folder ``project``:
|
||||
Start by creating a new folder::
|
||||
|
||||
.. code-block:: bash
|
||||
manim init project my-project --default
|
||||
|
||||
project/
|
||||
|
||||
This folder is the root folder for your project. It contains all the files that Manim needs to function,
|
||||
The ``my-project`` folder is the root folder for your project. It contains all the files that Manim needs to function,
|
||||
as well as any output that your project produces.
|
||||
|
||||
|
||||
Animating a circle
|
||||
******************
|
||||
|
||||
1. Open a text editor, such as Notepad. Copy the following code snippet into the window:
|
||||
1. Open a text editor, such as Notepad. Open the file ``main.py`` in the ``my-project`` folder.
|
||||
It should look something like this:
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: python
|
||||
|
||||
from manim import *
|
||||
from manim import *
|
||||
|
||||
|
||||
class CreateCircle(Scene):
|
||||
def construct(self):
|
||||
circle = Circle() # create a circle
|
||||
circle.set_fill(PINK, opacity=0.5) # set the color and transparency
|
||||
self.play(Create(circle)) # show the circle on screen
|
||||
class CreateCircle(Scene):
|
||||
def construct(self):
|
||||
circle = Circle() # create a circle
|
||||
circle.set_fill(PINK, opacity=0.5) # set the color and transparency
|
||||
self.play(Create(circle)) # show the circle on screen
|
||||
|
||||
2. Save the code snippet into your project folder with the name ``scene.py``.
|
||||
|
||||
.. code-block:: bash
|
||||
2. Open the command line, navigate to your project folder, and execute
|
||||
the following command:
|
||||
|
||||
project/
|
||||
└─scene.py
|
||||
.. code-block:: bash
|
||||
|
||||
3. Open the command line, navigate to your project folder, and execute
|
||||
the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
manim -pql scene.py CreateCircle
|
||||
manim -pql main.py CreateCircle
|
||||
|
||||
Manim will output rendering information, then create an MP4 file.
|
||||
Your default movie player will play the MP4 file, displaying the following animation.
|
||||
|
|
@ -113,7 +116,7 @@ Now let's look at the next two lines:
|
|||
|
||||
class CreateCircle(Scene):
|
||||
def construct(self):
|
||||
...
|
||||
[...]
|
||||
|
||||
Most of the time, the code for scripting an animation is entirely contained within
|
||||
the :meth:`~.Scene.construct` method of a :class:`.Scene` class.
|
||||
|
|
@ -192,7 +195,7 @@ Positioning ``Mobject``\s
|
|||
|
||||
Next, let's go over some basic techniques for positioning ``Mobject``\s.
|
||||
|
||||
1. Open ``scene.py``, and add the following code snippet below the ``SquareToCircle`` method:
|
||||
1. Open ``scene.py``, and add the following code snippet below the ``SquareToCircle`` class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
@ -268,11 +271,9 @@ and animating those method calls with ``.animate``.
|
|||
|
||||
self.play(Create(square)) # show the square on screen
|
||||
self.play(square.animate.rotate(PI / 4)) # rotate the square
|
||||
self.play(Transform(square, circle)) # transform the square into a circle
|
||||
self.play(
|
||||
ReplacementTransform(square, circle)
|
||||
) # transform the square into a circle
|
||||
self.play(
|
||||
circle.animate.set_fill(PINK, opacity=0.5)
|
||||
square.animate.set_fill(PINK, opacity=0.5)
|
||||
) # color the circle on screen
|
||||
|
||||
2. Render ``AnimatedSquareToCircle`` by running the following command in the command line:
|
||||
|
|
@ -293,8 +294,8 @@ The following animation will render:
|
|||
|
||||
self.play(Create(square)) # show the square on screen
|
||||
self.play(square.animate.rotate(PI / 4)) # rotate the square
|
||||
self.play(ReplacementTransform(square, circle)) # transform the square into a circle
|
||||
self.play(circle.animate.set_fill(PINK, opacity=0.5)) # color the circle on screen
|
||||
self.play(Transform(square, circle)) # transform the square into a circle
|
||||
self.play(square.animate.set_fill(PINK, opacity=0.5)) # color the circle on screen
|
||||
|
||||
The first ``self.play`` creates the square. The second animates rotating it 45 degrees.
|
||||
The third transforms the square into a circle, and the last colors the circle pink.
|
||||
|
|
@ -342,12 +343,61 @@ the corners of the square appear to contract slightly as they move into the posi
|
|||
for the first square to transform into the second one.
|
||||
|
||||
In ``DifferentRotations``, the difference between ``.animate``'s interpretation of rotation and the
|
||||
``Rotate`` method is far more apparent. The starting and ending states of a ``Mobject`` rotated 360 degrees
|
||||
``Rotate`` method is far more apparent. The starting and ending states of a ``Mobject`` rotated 180 degrees
|
||||
are the same, so ``.animate`` tries to interpolate two identical objects and the result is the left square.
|
||||
If you find that your own usage of ``.animate`` is causing similar unwanted behavior, consider
|
||||
using conventional animation methods like the right square, which uses ``Rotate``.
|
||||
|
||||
|
||||
``Transform`` vs ``ReplacementTransform``
|
||||
*****************************************
|
||||
The difference between ``Transform`` and ``ReplacementTransform`` is that ``Transform(mob1, mob2)`` transforms the points
|
||||
(as well as other attributes like color) of ``mob1`` into the points/attributes of ``mob2``.
|
||||
|
||||
``ReplacementTransform(mob1, mob2)`` on the other hand literally replaces ``mob1`` on the scene with ``mob2``.
|
||||
|
||||
The use of ``ReplacementTransform`` or ``Transform`` is mostly up to personal preference. They can be used to accomplish the same effect, as shown below.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class TwoTransforms(Scene):
|
||||
def transform(self):
|
||||
a = Circle()
|
||||
b = Square()
|
||||
c = Triangle()
|
||||
self.play(Transform(a, b))
|
||||
self.play(Transform(a, c))
|
||||
self.play(FadeOut(a))
|
||||
|
||||
def replacement_transform(self):
|
||||
a = Circle()
|
||||
b = Square()
|
||||
c = Triangle()
|
||||
self.play(ReplacementTransform(a, b))
|
||||
self.play(ReplacementTransform(b, c))
|
||||
self.play(FadeOut(c))
|
||||
|
||||
def construct(self):
|
||||
self.transform()
|
||||
self.wait(0.5) # wait for 0.5 seconds
|
||||
self.replacement_transform()
|
||||
|
||||
|
||||
However, in some cases it is more beneficial to use ``Transform``, like when you are transforming several mobjects one after the other.
|
||||
The code below avoids having to keep a reference to the last mobject that was transformed.
|
||||
|
||||
.. manim:: TransformCycle
|
||||
|
||||
class TransformCycle(Scene):
|
||||
def construct(self):
|
||||
a = Circle()
|
||||
t1 = Square()
|
||||
t2 = Triangle()
|
||||
self.add(a)
|
||||
self.wait()
|
||||
for t in [t1,t2]:
|
||||
self.play(Transform(a,t))
|
||||
|
||||
************
|
||||
You're done!
|
||||
************
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ def FrenchCursive(*tex_strings, **kwargs):
|
|||
|
||||
class TexFontTemplateManual(Scene):
|
||||
"""An example scene that uses a manually defined TexTemplate() object to create
|
||||
LaTeX output in French Cursive font"""
|
||||
LaTeX output in French Cursive font
|
||||
"""
|
||||
|
||||
def construct(self):
|
||||
self.add(Tex("Tex Font Example").to_edge(UL))
|
||||
|
|
@ -51,7 +52,7 @@ class TexFontTemplateLibrary(Scene):
|
|||
Many of the in the TexFontTemplates collection require that specific fonts
|
||||
are installed on your local machine.
|
||||
For example, choosing the template TexFontTemplates.comic_sans will
|
||||
not compile if the Comic Sans Micrososft font is not installed.
|
||||
not compile if the Comic Sans Microsoft font is not installed.
|
||||
|
||||
This scene will only render those Templates that do not cause a TeX
|
||||
compilation error on your system. Furthermore, some of the ones that do render,
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ class SpiralInExample(Scene):
|
|||
],
|
||||
color=PURPLE_B,
|
||||
fill_opacity=1,
|
||||
stroke_width=0
|
||||
stroke_width=0,
|
||||
).shift(UP + 2 * RIGHT)
|
||||
shapes = VGroup(triangle, square, circle, pentagon, pi)
|
||||
self.play(SpiralIn(shapes, fade_in_fraction=0.9))
|
||||
|
|
@ -167,8 +167,8 @@ Triangle.set_default(stroke_width=20)
|
|||
class LineJoints(Scene):
|
||||
def construct(self):
|
||||
t1 = Triangle()
|
||||
t2 = Triangle(line_join=LineJointType.ROUND)
|
||||
t3 = Triangle(line_join=LineJointType.BEVEL)
|
||||
t2 = Triangle(joint_type=LineJointType.ROUND)
|
||||
t3 = Triangle(joint_type=LineJointType.BEVEL)
|
||||
|
||||
grp = VGroup(t1, t2, t3).arrange(RIGHT)
|
||||
grp.set(width=config.frame_width - 1)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from pathlib import Path
|
|||
|
||||
import manim.utils.opengl as opengl
|
||||
from manim import *
|
||||
from manim.opengl import * # type: ignore
|
||||
from manim.opengl import *
|
||||
|
||||
# Copied from https://3b1b.github.io/manim/getting_started/example_scenes.html#surfaceexample.
|
||||
# Lines that do not yet work with the Community Version are commented.
|
||||
|
|
@ -406,7 +406,7 @@ class InteractiveDevelopment(Scene):
|
|||
self.play(Create(square))
|
||||
self.wait()
|
||||
|
||||
# This opens an iPython termnial where you can keep writing
|
||||
# This opens an iPython terminal where you can keep writing
|
||||
# lines as if they were part of this construct method.
|
||||
# In particular, 'square', 'circle' and 'self' will all be
|
||||
# part of the local namespace in that terminal.
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pkg_resources
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
|
||||
__version__: str = pkg_resources.get_distribution(__name__).version
|
||||
# Use installed distribution version if available; otherwise fall back to a
|
||||
# sensible default so that importing from a source checkout works without an
|
||||
# editable install (pip install -e .).
|
||||
try:
|
||||
__version__ = version(__name__)
|
||||
except PackageNotFoundError:
|
||||
# Package is not installed; provide a fallback version string.
|
||||
__version__ = "0.0.0+unknown"
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
# isort: off
|
||||
|
||||
# Importing the config module should be the first thing we do, since other
|
||||
|
|
@ -20,6 +23,7 @@ from ._config import *
|
|||
from .utils.commands import *
|
||||
|
||||
# isort: on
|
||||
import numpy as np
|
||||
|
||||
from .animation.animation import *
|
||||
from .animation.changing import *
|
||||
|
|
@ -69,6 +73,7 @@ from .mobject.text.code_mobject import *
|
|||
from .mobject.text.numbers import *
|
||||
from .mobject.text.tex_mobject import *
|
||||
from .mobject.text.text_mobject import *
|
||||
from .mobject.text.typst_mobject import *
|
||||
from .mobject.three_d.polyhedra import *
|
||||
from .mobject.three_d.three_d_utils import *
|
||||
from .mobject.three_d.three_dimensions import *
|
||||
|
|
|
|||
|
|
@ -1,27 +1,54 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
import click
|
||||
import cloup
|
||||
|
||||
from . import __version__, cli_ctx_settings, console
|
||||
from .cli.cfg.group import cfg
|
||||
from .cli.checkhealth.commands import checkhealth
|
||||
from .cli.default_group import DefaultGroup
|
||||
from .cli.init.commands import init
|
||||
from .cli.new.group import new
|
||||
from .cli.plugins.commands import plugins
|
||||
from .cli.render.commands import render
|
||||
from .constants import EPILOG
|
||||
from manim import __version__
|
||||
from manim._config import cli_ctx_settings, console
|
||||
from manim.cli.cfg.group import cfg
|
||||
from manim.cli.checkhealth.commands import checkhealth
|
||||
from manim.cli.default_group import DefaultGroup
|
||||
from manim.cli.init.commands import init
|
||||
from manim.cli.plugins.commands import plugins
|
||||
from manim.cli.render.commands import render
|
||||
from manim.constants import EPILOG
|
||||
|
||||
|
||||
def exit_early(ctx, param, value):
|
||||
def show_splash(ctx: click.Context, param: click.Option, value: str | None) -> None:
|
||||
"""When giving a value by console, show an initial message with the Manim
|
||||
version before executing any other command: ``Manim Community vA.B.C``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ctx
|
||||
The Click context.
|
||||
param
|
||||
A Click option.
|
||||
value
|
||||
A string value given by console, or None.
|
||||
"""
|
||||
if value:
|
||||
sys.exit()
|
||||
console.print(f"Manim Community [green]v{__version__}[/green]\n")
|
||||
|
||||
|
||||
console.print(f"Manim Community [green]v{__version__}[/green]\n")
|
||||
def print_version_and_exit(
|
||||
ctx: click.Context, param: click.Option, value: str | None
|
||||
) -> None:
|
||||
"""Same as :func:`show_splash`, but also exit when giving a value by
|
||||
console.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ctx
|
||||
The Click context.
|
||||
param
|
||||
A Click option.
|
||||
value
|
||||
A string value given by console, or None.
|
||||
"""
|
||||
show_splash(ctx, param, value)
|
||||
if value:
|
||||
ctx.exit()
|
||||
|
||||
|
||||
@cloup.group(
|
||||
|
|
@ -35,17 +62,32 @@ console.print(f"Manim Community [green]v{__version__}[/green]\n")
|
|||
"is specified. Run 'manim render --help' if you would like to know what the "
|
||||
f"'-ql' or '-p' flags do, for example.\n\n{EPILOG}",
|
||||
)
|
||||
@click.option(
|
||||
@cloup.option(
|
||||
"--version",
|
||||
is_flag=True,
|
||||
help="Show version and exit.",
|
||||
callback=exit_early,
|
||||
callback=print_version_and_exit,
|
||||
is_eager=True,
|
||||
expose_value=False,
|
||||
)
|
||||
@click.pass_context
|
||||
def main(ctx):
|
||||
"""The entry point for manim."""
|
||||
@click.option(
|
||||
"--show-splash/--hide-splash",
|
||||
is_flag=True,
|
||||
default=True,
|
||||
help="Print splash message with version information.",
|
||||
callback=show_splash,
|
||||
is_eager=True,
|
||||
expose_value=False,
|
||||
)
|
||||
@cloup.pass_context
|
||||
def main(ctx: click.Context) -> None:
|
||||
"""The entry point for Manim.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ctx
|
||||
The Click context.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -53,7 +95,6 @@ main.add_command(checkhealth)
|
|||
main.add_command(cfg)
|
||||
main.add_command(plugins)
|
||||
main.add_command(init)
|
||||
main.add_command(new)
|
||||
main.add_command(render)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from contextlib import _GeneratorContextManager, contextmanager
|
||||
from collections.abc import Generator
|
||||
from contextlib import contextmanager
|
||||
from typing import Any
|
||||
|
||||
from .cli_colors import parse_cli_ctx
|
||||
from .logger_utils import make_logger
|
||||
|
|
@ -21,10 +23,9 @@ __all__ = [
|
|||
|
||||
parser = make_config_parser()
|
||||
|
||||
# The logger can be accessed from anywhere as manim.logger, or as
|
||||
# logging.getLogger("manim"). The console must be accessed as manim.console.
|
||||
# Throughout the codebase, use manim.console.print() instead of print().
|
||||
# Use error_console to print errors so that it outputs to stderr.
|
||||
# Logger usage: accessible globally as `manim.logger` or via `logging.getLogger("manim")`.
|
||||
# For printing, use `manim.console.print()` instead of the built-in `print()`.
|
||||
# For error output, use `error_console`, which prints to stderr.
|
||||
logger, console, error_console = make_logger(
|
||||
parser["logger"],
|
||||
parser["CLI"]["verbosity"],
|
||||
|
|
@ -35,13 +36,15 @@ logging.getLogger("PIL").setLevel(logging.INFO)
|
|||
logging.getLogger("matplotlib").setLevel(logging.INFO)
|
||||
|
||||
config = ManimConfig().digest_parser(parser)
|
||||
# TODO: to be used in the future - see PR #620
|
||||
# https://github.com/ManimCommunity/manim/pull/620
|
||||
frame = ManimFrame(config)
|
||||
|
||||
|
||||
# This has to go here because it needs access to this module's config
|
||||
@contextmanager
|
||||
def tempconfig(temp: ManimConfig | dict) -> _GeneratorContextManager:
|
||||
"""Context manager that temporarily modifies the global ``config`` object.
|
||||
def tempconfig(temp: ManimConfig | dict[str, Any]) -> Generator[None, None, None]:
|
||||
"""Temporarily modifies the global ``config`` object using a context manager.
|
||||
|
||||
Inside the ``with`` statement, the modified config will be used. After
|
||||
context manager exits, the config will be restored to its original state.
|
||||
|
|
@ -64,7 +67,6 @@ def tempconfig(temp: ManimConfig | dict) -> _GeneratorContextManager:
|
|||
8.0
|
||||
>>> with tempconfig({"frame_height": 100.0}):
|
||||
... print(config["frame_height"])
|
||||
...
|
||||
100.0
|
||||
>>> config["frame_height"]
|
||||
8.0
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
"""Parses CLI context settings from the configuration file and returns a Cloup Context settings dictionary.
|
||||
|
||||
This module reads configuration values for help formatting, theme styles, and alignment options
|
||||
used when rendering command-line interfaces in Manim.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import configparser
|
||||
from typing import Any
|
||||
|
||||
from cloup import Context, HelpFormatter, HelpTheme, Style
|
||||
|
||||
__all__ = ["parse_cli_ctx"]
|
||||
|
||||
def parse_cli_ctx(parser: configparser.ConfigParser) -> Context:
|
||||
formatter_settings = {
|
||||
|
||||
def parse_cli_ctx(parser: configparser.SectionProxy) -> dict[str, Any]:
|
||||
formatter_settings: dict[str, str | int | None] = {
|
||||
"indent_increment": int(parser["indent_increment"]),
|
||||
"width": int(parser["width"]),
|
||||
"col1_max_width": int(parser["col1_max_width"]),
|
||||
|
|
@ -23,6 +34,7 @@ def parse_cli_ctx(parser: configparser.ConfigParser) -> Context:
|
|||
"col2",
|
||||
"epilog",
|
||||
}
|
||||
# Extract and apply any style-related keys defined in the config section.
|
||||
for k, v in parser.items():
|
||||
if k in theme_keys and v:
|
||||
theme_settings.update({k: Style(v)})
|
||||
|
|
@ -30,21 +42,26 @@ def parse_cli_ctx(parser: configparser.ConfigParser) -> Context:
|
|||
formatter = {}
|
||||
theme = parser["theme"] if parser["theme"] else None
|
||||
if theme is None:
|
||||
formatter = HelpFormatter().settings(
|
||||
theme=HelpTheme(**theme_settings), **formatter_settings
|
||||
formatter = HelpFormatter.settings(
|
||||
theme=HelpTheme(**theme_settings),
|
||||
**formatter_settings,
|
||||
)
|
||||
elif theme.lower() == "dark":
|
||||
formatter = HelpFormatter().settings(
|
||||
theme=HelpTheme.dark().with_(**theme_settings), **formatter_settings
|
||||
formatter = HelpFormatter.settings(
|
||||
theme=HelpTheme.dark().with_(**theme_settings),
|
||||
**formatter_settings,
|
||||
)
|
||||
elif theme.lower() == "light":
|
||||
formatter = HelpFormatter().settings(
|
||||
theme=HelpTheme.light().with_(**theme_settings), **formatter_settings
|
||||
formatter = HelpFormatter.settings(
|
||||
theme=HelpTheme.light().with_(**theme_settings),
|
||||
**formatter_settings,
|
||||
)
|
||||
|
||||
return Context.settings(
|
||||
return_val: dict[str, Any] = Context.settings(
|
||||
align_option_groups=parser["align_option_groups"].lower() == "true",
|
||||
align_sections=parser["align_sections"].lower() == "true",
|
||||
show_constraints=True,
|
||||
formatter_settings=formatter,
|
||||
)
|
||||
|
||||
return return_val
|
||||
|
|
|
|||
|
|
@ -221,9 +221,7 @@ repr_number = green
|
|||
# Uncomment the following line to manually set the loglevel for ffmpeg. See
|
||||
# ffmpeg manpage for accepted values
|
||||
loglevel = ERROR
|
||||
# defaults to the one present in path
|
||||
ffmpeg_executable = ffmpeg
|
||||
|
||||
[jupyter]
|
||||
media_embed =
|
||||
media_embed = False
|
||||
media_width = 60%%
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ Both ``logger`` and ``console`` use the ``rich`` library to produce rich text
|
|||
format.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import configparser
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from rich import color, errors
|
||||
from rich import print as printf
|
||||
|
|
@ -27,6 +27,8 @@ from rich.theme import Theme
|
|||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
__all__ = ["make_logger", "parse_theme", "set_file_logger", "JSONFormatter"]
|
||||
|
||||
HIGHLIGHTED_KEYWORDS = [ # these keywords are highlighted specially
|
||||
"Played",
|
||||
"animations",
|
||||
|
|
@ -50,9 +52,9 @@ Loading the default color configuration.[/logging.level.error]
|
|||
|
||||
|
||||
def make_logger(
|
||||
parser: configparser.ConfigParser,
|
||||
parser: configparser.SectionProxy,
|
||||
verbosity: str,
|
||||
) -> tuple[logging.Logger, Console]:
|
||||
) -> tuple[logging.Logger, Console, Console]:
|
||||
"""Make the manim logger and console.
|
||||
|
||||
Parameters
|
||||
|
|
@ -84,25 +86,29 @@ def make_logger(
|
|||
theme = parse_theme(parser)
|
||||
console = Console(theme=theme)
|
||||
|
||||
# With rich 9.5.0+ we could pass stderr=True instead
|
||||
error_console = Console(theme=theme, file=sys.stderr)
|
||||
error_console = Console(theme=theme, stderr=True)
|
||||
|
||||
# set the rich handler
|
||||
RichHandler.KEYWORDS = HIGHLIGHTED_KEYWORDS
|
||||
rich_handler = RichHandler(
|
||||
console=console,
|
||||
show_time=parser.getboolean("log_timestamps"),
|
||||
show_time=parser.getboolean("log_timestamps", fallback=False),
|
||||
keywords=HIGHLIGHTED_KEYWORDS,
|
||||
)
|
||||
|
||||
# finally, the logger
|
||||
logger = logging.getLogger("manim")
|
||||
logger.addHandler(rich_handler)
|
||||
logger.setLevel(verbosity)
|
||||
logger.propagate = False
|
||||
|
||||
if not (libav_logger := logging.getLogger()).hasHandlers():
|
||||
libav_logger.addHandler(rich_handler)
|
||||
libav_logger.setLevel(verbosity)
|
||||
|
||||
return logger, console, error_console
|
||||
|
||||
|
||||
def parse_theme(parser: configparser.ConfigParser) -> Theme:
|
||||
def parse_theme(parser: configparser.SectionProxy) -> Theme | None:
|
||||
"""Configure the rich style of logger and console output.
|
||||
|
||||
Parameters
|
||||
|
|
@ -120,7 +126,7 @@ def parse_theme(parser: configparser.ConfigParser) -> Theme:
|
|||
:func:`make_logger`.
|
||||
|
||||
"""
|
||||
theme = {key.replace("_", "."): parser[key] for key in parser}
|
||||
theme: dict[str, Any] = {key.replace("_", "."): parser[key] for key in parser}
|
||||
|
||||
theme["log.width"] = None if theme["log.width"] == "-1" else int(theme["log.width"])
|
||||
theme["log.height"] = (
|
||||
|
|
@ -178,12 +184,15 @@ class JSONFormatter(logging.Formatter):
|
|||
|
||||
"""
|
||||
|
||||
def format(self, record: dict) -> str:
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
"""Format the record in a custom JSON format."""
|
||||
record_c = copy.deepcopy(record)
|
||||
if record_c.args:
|
||||
for arg in record_c.args:
|
||||
record_c.args[arg] = "<>"
|
||||
if isinstance(record_c.args, dict):
|
||||
for arg in record_c.args:
|
||||
record_c.args[arg] = "<>"
|
||||
else:
|
||||
record_c.args = ("<>",) * len(record_c.args)
|
||||
return json.dumps(
|
||||
{
|
||||
"levelname": record_c.levelname,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,5 @@
|
|||
"""Animate mobjects."""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
||||
|
|
@ -8,15 +7,17 @@ from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
|||
from .. import config, logger
|
||||
from ..constants import RendererType
|
||||
from ..mobject import mobject
|
||||
from ..mobject.mobject import Mobject
|
||||
from ..mobject.mobject import Group, Mobject
|
||||
from ..mobject.opengl import opengl_mobject
|
||||
from ..utils.rate_functions import linear, smooth
|
||||
|
||||
__all__ = ["Animation", "Wait", "override_animation"]
|
||||
__all__ = ["Animation", "Wait", "Add", "override_animation"]
|
||||
|
||||
|
||||
from collections.abc import Callable, Iterable, Sequence
|
||||
from copy import deepcopy
|
||||
from typing import TYPE_CHECKING, Callable, Iterable, Sequence
|
||||
from functools import partialmethod
|
||||
from typing import TYPE_CHECKING, Any, Self
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.scene.scene import Scene
|
||||
|
|
@ -51,7 +52,6 @@ class Animation:
|
|||
For example ``rate_func(0.5)`` is the proportion of the animation that is done
|
||||
after half of the animations run time.
|
||||
|
||||
|
||||
reverse_rate_function
|
||||
Reverses the rate function of the animation. Setting ``reverse_rate_function``
|
||||
does not have any effect on ``remover`` or ``introducer``. These need to be
|
||||
|
|
@ -112,13 +112,13 @@ class Animation:
|
|||
*args,
|
||||
use_override=True,
|
||||
**kwargs,
|
||||
):
|
||||
) -> Self:
|
||||
if isinstance(mobject, Mobject) and use_override:
|
||||
func = mobject.animation_override_for(cls)
|
||||
if func is not None:
|
||||
anim = func(mobject, *args, **kwargs)
|
||||
logger.debug(
|
||||
f"The {cls.__name__} animation has been is overridden for "
|
||||
f"The {cls.__name__} animation has been overridden for "
|
||||
f"{type(mobject).__name__} mobjects. use_override = False can "
|
||||
f" be used as keyword argument to prevent animation overriding.",
|
||||
)
|
||||
|
|
@ -127,7 +127,7 @@ class Animation:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: Mobject | None,
|
||||
mobject: Mobject | OpenGLMobject | None,
|
||||
lag_ratio: float = DEFAULT_ANIMATION_LAG_RATIO,
|
||||
run_time: float = DEFAULT_ANIMATION_RUN_TIME,
|
||||
rate_func: Callable[[float], float] = smooth,
|
||||
|
|
@ -137,8 +137,8 @@ class Animation:
|
|||
suspend_mobject_updating: bool = True,
|
||||
introducer: bool = False,
|
||||
*,
|
||||
_on_finish: Callable[[], None] = lambda _: None,
|
||||
**kwargs,
|
||||
_on_finish: Callable[[Scene], None] = lambda _: None,
|
||||
use_override: bool = True, # included here to avoid TypeError if passed from a subclass' constructor
|
||||
) -> None:
|
||||
self._typecheck_input(mobject)
|
||||
self.run_time: float = run_time
|
||||
|
|
@ -158,8 +158,6 @@ class Animation:
|
|||
else:
|
||||
self.starting_mobject: Mobject = Mobject()
|
||||
self.mobject: Mobject = mobject if mobject is not None else Mobject()
|
||||
if kwargs:
|
||||
logger.debug("Animation received extra kwargs: %s", kwargs)
|
||||
|
||||
if hasattr(self, "CONFIG"):
|
||||
logger.error(
|
||||
|
|
@ -169,6 +167,19 @@ class Animation:
|
|||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def run_time(self) -> float:
|
||||
return self._run_time
|
||||
|
||||
@run_time.setter
|
||||
def run_time(self, value: float) -> None:
|
||||
if value < 0:
|
||||
raise ValueError(
|
||||
f"The run_time of {self.__class__.__name__} cannot be "
|
||||
f"negative. The given value was {value}."
|
||||
)
|
||||
self._run_time = value
|
||||
|
||||
def _typecheck_input(self, mobject: Mobject | None) -> None:
|
||||
if mobject is None:
|
||||
logger.debug("Animation with empty mobject")
|
||||
|
|
@ -249,11 +260,11 @@ class Animation:
|
|||
):
|
||||
scene.add(self.mobject)
|
||||
|
||||
def create_starting_mobject(self) -> Mobject:
|
||||
def create_starting_mobject(self) -> Mobject | OpenGLMobject:
|
||||
# Keep track of where the mobject starts
|
||||
return self.mobject.copy()
|
||||
|
||||
def get_all_mobjects(self) -> Sequence[Mobject]:
|
||||
def get_all_mobjects(self) -> Sequence[Mobject | OpenGLMobject]:
|
||||
"""Get all mobjects involved in the animation.
|
||||
|
||||
Ordering must match the ordering of arguments to interpolate_submobject
|
||||
|
|
@ -267,9 +278,12 @@ class Animation:
|
|||
|
||||
def get_all_families_zipped(self) -> Iterable[tuple]:
|
||||
if config["renderer"] == RendererType.OPENGL:
|
||||
return zip(*(mob.get_family() for mob in self.get_all_mobjects()))
|
||||
return zip(
|
||||
*(mob.get_family() for mob in self.get_all_mobjects()), strict=False
|
||||
)
|
||||
return zip(
|
||||
*(mob.family_members_with_points() for mob in self.get_all_mobjects())
|
||||
*(mob.family_members_with_points() for mob in self.get_all_mobjects()),
|
||||
strict=False,
|
||||
)
|
||||
|
||||
def update_mobjects(self, dt: float) -> None:
|
||||
|
|
@ -398,6 +412,7 @@ class Animation:
|
|||
self.run_time = run_time
|
||||
return self
|
||||
|
||||
# TODO: is this getter even necessary?
|
||||
def get_run_time(self) -> float:
|
||||
"""Get the run time of the animation.
|
||||
|
||||
|
|
@ -476,9 +491,57 @@ class Animation:
|
|||
"""
|
||||
return self.introducer
|
||||
|
||||
@classmethod
|
||||
def __init_subclass__(cls, **kwargs) -> None:
|
||||
super().__init_subclass__(**kwargs)
|
||||
|
||||
cls._original__init__ = cls.__init__
|
||||
|
||||
_original__init__ = __init__ # needed if set_default() is called with no kwargs directly from Animation
|
||||
|
||||
@classmethod
|
||||
def set_default(cls, **kwargs) -> None:
|
||||
"""Sets the default values of keyword arguments.
|
||||
|
||||
If this method is called without any additional keyword
|
||||
arguments, the original default values of the initialization
|
||||
method of this class are restored.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
kwargs
|
||||
Passing any keyword argument will update the default
|
||||
values of the keyword arguments of the initialization
|
||||
function of this class.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: ChangeDefaultAnimation
|
||||
|
||||
class ChangeDefaultAnimation(Scene):
|
||||
def construct(self):
|
||||
Rotate.set_default(run_time=2, rate_func=rate_functions.linear)
|
||||
Indicate.set_default(color=None)
|
||||
|
||||
S = Square(color=BLUE, fill_color=BLUE, fill_opacity=0.25)
|
||||
self.add(S)
|
||||
self.play(Rotate(S, PI))
|
||||
self.play(Indicate(S))
|
||||
|
||||
Rotate.set_default()
|
||||
Indicate.set_default()
|
||||
|
||||
"""
|
||||
if kwargs:
|
||||
cls.__init__ = partialmethod(cls.__init__, **kwargs)
|
||||
else:
|
||||
cls.__init__ = cls._original__init__
|
||||
|
||||
|
||||
def prepare_animation(
|
||||
anim: Animation | mobject._AnimationBuilder,
|
||||
anim: Animation | mobject._AnimationBuilder | opengl_mobject._AnimationBuilder,
|
||||
) -> Animation:
|
||||
r"""Returns either an unchanged animation, or the animation built
|
||||
from a passed animation factory.
|
||||
|
|
@ -575,6 +638,90 @@ class Wait(Animation):
|
|||
pass
|
||||
|
||||
|
||||
class Add(Animation):
|
||||
"""Add Mobjects to a scene, without animating them in any other way. This
|
||||
is similar to the :meth:`.Scene.add` method, but :class:`Add` is an
|
||||
animation which can be grouped into other animations.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mobjects
|
||||
One :class:`~.Mobject` or more to add to a scene.
|
||||
run_time
|
||||
The duration of the animation after adding the ``mobjects``. Defaults
|
||||
to 0, which means this is an instant animation without extra wait time
|
||||
after adding them.
|
||||
**kwargs
|
||||
Additional arguments to pass to the parent :class:`Animation` class.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: DefaultAddScene
|
||||
|
||||
class DefaultAddScene(Scene):
|
||||
def construct(self):
|
||||
text_1 = Text("I was added with Add!")
|
||||
text_2 = Text("Me too!")
|
||||
text_3 = Text("And me!")
|
||||
texts = VGroup(text_1, text_2, text_3).arrange(DOWN)
|
||||
rect = SurroundingRectangle(texts, buff=0.5)
|
||||
|
||||
self.play(
|
||||
Create(rect, run_time=3.0),
|
||||
Succession(
|
||||
Wait(1.0),
|
||||
# You can Add a Mobject in the middle of an animation...
|
||||
Add(text_1),
|
||||
Wait(1.0),
|
||||
# ...or multiple Mobjects at once!
|
||||
Add(text_2, text_3),
|
||||
),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
.. manim:: AddWithRunTimeScene
|
||||
|
||||
class AddWithRunTimeScene(Scene):
|
||||
def construct(self):
|
||||
# A 5x5 grid of circles
|
||||
circles = VGroup(
|
||||
*[Circle(radius=0.5) for _ in range(25)]
|
||||
).arrange_in_grid(5, 5)
|
||||
|
||||
self.play(
|
||||
Succession(
|
||||
# Add a run_time of 0.2 to wait for 0.2 seconds after
|
||||
# adding the circle, instead of using Wait(0.2) after Add!
|
||||
*[Add(circle, run_time=0.2) for circle in circles],
|
||||
rate_func=smooth,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *mobjects: Mobject, run_time: float = 0.0, **kwargs: Any
|
||||
) -> None:
|
||||
mobject = mobjects[0] if len(mobjects) == 1 else Group(*mobjects)
|
||||
super().__init__(mobject, run_time=run_time, introducer=True, **kwargs)
|
||||
|
||||
def begin(self) -> None:
|
||||
pass
|
||||
|
||||
def finish(self) -> None:
|
||||
pass
|
||||
|
||||
def clean_up_from_scene(self, scene: Scene) -> None:
|
||||
pass
|
||||
|
||||
def update_mobjects(self, dt: float) -> None:
|
||||
pass
|
||||
|
||||
def interpolate(self, alpha: float) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def override_animation(
|
||||
animation_class: type[Animation],
|
||||
) -> Callable[[Callable], Callable]:
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ from __future__ import annotations
|
|||
|
||||
__all__ = ["AnimatedBoundary", "TracedPath"]
|
||||
|
||||
from typing import Callable
|
||||
from collections.abc import Callable, Sequence
|
||||
from typing import Any, Self
|
||||
|
||||
from manim.mobject.mobject import Mobject
|
||||
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
||||
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
|
||||
from manim.utils.color import (
|
||||
|
|
@ -16,7 +18,7 @@ from manim.utils.color import (
|
|||
WHITE,
|
||||
ParsableManimColor,
|
||||
)
|
||||
from manim.utils.rate_functions import smooth
|
||||
from manim.utils.rate_functions import RateFunction, smooth
|
||||
|
||||
|
||||
class AnimatedBoundary(VGroup):
|
||||
|
|
@ -38,14 +40,14 @@ class AnimatedBoundary(VGroup):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
vmobject,
|
||||
colors=[BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
|
||||
max_stroke_width=3,
|
||||
cycle_rate=0.5,
|
||||
back_and_forth=True,
|
||||
draw_rate_func=smooth,
|
||||
fade_rate_func=smooth,
|
||||
**kwargs,
|
||||
vmobject: VMobject,
|
||||
colors: Sequence[ParsableManimColor] = [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
|
||||
max_stroke_width: float = 3,
|
||||
cycle_rate: float = 0.5,
|
||||
back_and_forth: bool = True,
|
||||
draw_rate_func: RateFunction = smooth,
|
||||
fade_rate_func: RateFunction = smooth,
|
||||
**kwargs: Any,
|
||||
):
|
||||
super().__init__(**kwargs)
|
||||
self.colors = colors
|
||||
|
|
@ -59,10 +61,10 @@ class AnimatedBoundary(VGroup):
|
|||
vmobject.copy().set_style(stroke_width=0, fill_opacity=0) for x in range(2)
|
||||
]
|
||||
self.add(*self.boundary_copies)
|
||||
self.total_time = 0
|
||||
self.total_time = 0.0
|
||||
self.add_updater(lambda m, dt: self.update_boundary_copies(dt))
|
||||
|
||||
def update_boundary_copies(self, dt):
|
||||
def update_boundary_copies(self, dt: float) -> None:
|
||||
# Not actual time, but something which passes at
|
||||
# an altered rate to make the implementation below
|
||||
# cleaner
|
||||
|
|
@ -78,9 +80,9 @@ class AnimatedBoundary(VGroup):
|
|||
fade_alpha = self.fade_rate_func(alpha)
|
||||
|
||||
if self.back_and_forth and int(time) % 2 == 1:
|
||||
bounds = (1 - draw_alpha, 1)
|
||||
bounds = (1.0 - draw_alpha, 1.0)
|
||||
else:
|
||||
bounds = (0, draw_alpha)
|
||||
bounds = (0.0, draw_alpha)
|
||||
self.full_family_become_partial(growing, vmobject, *bounds)
|
||||
growing.set_stroke(colors[index], width=msw)
|
||||
|
||||
|
|
@ -90,10 +92,12 @@ class AnimatedBoundary(VGroup):
|
|||
|
||||
self.total_time += dt
|
||||
|
||||
def full_family_become_partial(self, mob1, mob2, a, b):
|
||||
def full_family_become_partial(
|
||||
self, mob1: VMobject, mob2: VMobject, a: float, b: float
|
||||
) -> Self:
|
||||
family1 = mob1.family_members_with_points()
|
||||
family2 = mob2.family_members_with_points()
|
||||
for sm1, sm2 in zip(family1, family2):
|
||||
for sm1, sm2 in zip(family1, family2, strict=False):
|
||||
sm1.pointwise_become_partial(sm2, a, b)
|
||||
return self
|
||||
|
||||
|
|
@ -146,20 +150,21 @@ class TracedPath(VMobject, metaclass=ConvertToOpenGL):
|
|||
stroke_width: float = 2,
|
||||
stroke_color: ParsableManimColor | None = WHITE,
|
||||
dissipating_time: float | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
super().__init__(stroke_color=stroke_color, stroke_width=stroke_width, **kwargs)
|
||||
self.traced_point_func = traced_point_func
|
||||
self.dissipating_time = dissipating_time
|
||||
self.time = 1 if self.dissipating_time else None
|
||||
self.time = 1.0 if self.dissipating_time else None
|
||||
self.add_updater(self.update_path)
|
||||
|
||||
def update_path(self, mob, dt):
|
||||
def update_path(self, mob: Mobject, dt: float) -> None:
|
||||
new_point = self.traced_point_func()
|
||||
if not self.has_points():
|
||||
self.start_new_path(new_point)
|
||||
self.add_line_to(new_point)
|
||||
if self.dissipating_time:
|
||||
assert self.time is not None
|
||||
self.time += dt
|
||||
if self.time - 1 > self.dissipating_time:
|
||||
nppcc = self.n_points_per_curve
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
"""Tools for displaying multiple animations at once."""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Callable, Sequence
|
||||
from collections.abc import Callable, Iterable, Sequence
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLGroup
|
||||
|
||||
from .._config import config
|
||||
from ..animation.animation import Animation, prepare_animation
|
||||
from ..constants import RendererType
|
||||
from ..mobject.mobject import Group, Mobject
|
||||
from ..scene.scene import Scene
|
||||
from ..utils.iterables import remove_list_redundancies
|
||||
from ..utils.rate_functions import linear
|
||||
from manim._config import config
|
||||
from manim.animation.animation import Animation, prepare_animation
|
||||
from manim.constants import RendererType
|
||||
from manim.mobject.mobject import Group, Mobject
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
|
||||
from manim.scene.scene import Scene
|
||||
from manim.utils.iterables import remove_list_redundancies
|
||||
from manim.utils.parameter_parsing import flatten_iterable_parameters
|
||||
from manim.utils.rate_functions import linear
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup
|
||||
|
||||
from ..mobject.types.vectorized_mobject import VGroup
|
||||
from manim.mobject.types.vectorized_mobject import VGroup
|
||||
|
||||
__all__ = ["AnimationGroup", "Succession", "LaggedStart", "LaggedStartMap"]
|
||||
|
||||
|
|
@ -54,45 +53,57 @@ class AnimationGroup(Animation):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
*animations: Animation,
|
||||
group: Group | VGroup | OpenGLGroup | OpenGLVGroup = None,
|
||||
*animations: Animation | Iterable[Animation],
|
||||
group: Group | VGroup | OpenGLGroup | OpenGLVGroup | None = None,
|
||||
run_time: float | None = None,
|
||||
rate_func: Callable[[float], float] = linear,
|
||||
lag_ratio: float = 0,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.animations = [prepare_animation(anim) for anim in animations]
|
||||
**kwargs: Any,
|
||||
):
|
||||
arg_anim = flatten_iterable_parameters(animations)
|
||||
self.animations = [prepare_animation(anim) for anim in arg_anim]
|
||||
self.rate_func = rate_func
|
||||
self.group = group
|
||||
if self.group is None:
|
||||
if group is None:
|
||||
mobjects = remove_list_redundancies(
|
||||
[anim.mobject for anim in self.animations if not anim.is_introducer()],
|
||||
)
|
||||
if config["renderer"] == RendererType.OPENGL:
|
||||
self.group = OpenGLGroup(*mobjects)
|
||||
self.group: Group | VGroup | OpenGLGroup | OpenGLVGroup = OpenGLGroup(
|
||||
*mobjects
|
||||
)
|
||||
else:
|
||||
self.group = Group(*mobjects)
|
||||
else:
|
||||
self.group = group
|
||||
super().__init__(
|
||||
self.group, rate_func=self.rate_func, lag_ratio=lag_ratio, **kwargs
|
||||
)
|
||||
self.run_time: float = self.init_run_time(run_time)
|
||||
|
||||
def get_all_mobjects(self) -> Sequence[Mobject]:
|
||||
def get_all_mobjects(self) -> Sequence[Mobject | OpenGLMobject]:
|
||||
return list(self.group)
|
||||
|
||||
def begin(self) -> None:
|
||||
if not self.animations:
|
||||
raise ValueError(
|
||||
f"Trying to play {self} without animations, this is not supported. "
|
||||
"Please add at least one subanimation."
|
||||
)
|
||||
self.anim_group_time = 0.0
|
||||
if self.suspend_mobject_updating:
|
||||
self.group.suspend_updating()
|
||||
for anim in self.animations:
|
||||
anim.begin()
|
||||
|
||||
def _setup_scene(self, scene) -> None:
|
||||
def _setup_scene(self, scene: Scene) -> None:
|
||||
for anim in self.animations:
|
||||
anim._setup_scene(scene)
|
||||
|
||||
def finish(self) -> None:
|
||||
for anim in self.animations:
|
||||
anim.finish()
|
||||
self.anims_begun[:] = True
|
||||
self.anims_finished[:] = True
|
||||
if self.suspend_mobject_updating:
|
||||
self.group.resume_updating()
|
||||
|
||||
|
|
@ -104,10 +115,12 @@ class AnimationGroup(Animation):
|
|||
anim.clean_up_from_scene(scene)
|
||||
|
||||
def update_mobjects(self, dt: float) -> None:
|
||||
for anim in self.animations:
|
||||
for anim in self.anims_with_timings["anim"][
|
||||
self.anims_begun & ~self.anims_finished
|
||||
]:
|
||||
anim.update_mobjects(dt)
|
||||
|
||||
def init_run_time(self, run_time) -> float:
|
||||
def init_run_time(self, run_time: float | None) -> float:
|
||||
"""Calculates the run time of the animation, if different from ``run_time``.
|
||||
|
||||
Parameters
|
||||
|
|
@ -121,22 +134,30 @@ class AnimationGroup(Animation):
|
|||
The duration of the animation in seconds.
|
||||
"""
|
||||
self.build_animations_with_timings()
|
||||
if self.anims_with_timings:
|
||||
self.max_end_time = np.max([awt[2] for awt in self.anims_with_timings])
|
||||
else:
|
||||
self.max_end_time = 0
|
||||
# Note: if lag_ratio < 1, then not necessarily the final animation's
|
||||
# end time will be the max end time! Therefore we must calculate the
|
||||
# maximum over all the end times, and not just take the last one.
|
||||
# Example: if you want to play 2 animations of 10s and 1s with a
|
||||
# lag_ratio of 0.1, the 1st one will end at t=10 and the 2nd one will
|
||||
# end at t=2, so the AnimationGroup will end at t=10.
|
||||
self.max_end_time = max(self.anims_with_timings["end"], default=0)
|
||||
return self.max_end_time if run_time is None else run_time
|
||||
|
||||
def build_animations_with_timings(self) -> None:
|
||||
"""Creates a list of triplets of the form (anim, start_time, end_time)."""
|
||||
self.anims_with_timings = []
|
||||
curr_time: float = 0
|
||||
for anim in self.animations:
|
||||
start_time: float = curr_time
|
||||
end_time: float = start_time + anim.get_run_time()
|
||||
self.anims_with_timings.append((anim, start_time, end_time))
|
||||
# Start time of next animation is based on the lag_ratio
|
||||
curr_time = (1 - self.lag_ratio) * start_time + self.lag_ratio * end_time
|
||||
run_times = np.array([anim.run_time for anim in self.animations])
|
||||
num_animations = run_times.shape[0]
|
||||
dtype = [("anim", "O"), ("start", "f8"), ("end", "f8")]
|
||||
self.anims_with_timings: np.ndarray = np.zeros(num_animations, dtype=dtype)
|
||||
self.anims_begun: np.ndarray = np.zeros(num_animations, dtype=bool)
|
||||
self.anims_finished: np.ndarray = np.zeros(num_animations, dtype=bool)
|
||||
if num_animations == 0:
|
||||
return
|
||||
|
||||
lags = run_times[:-1] * self.lag_ratio
|
||||
self.anims_with_timings["anim"] = self.animations
|
||||
self.anims_with_timings["start"][1:] = np.add.accumulate(lags)
|
||||
self.anims_with_timings["end"] = self.anims_with_timings["start"] + run_times
|
||||
|
||||
def interpolate(self, alpha: float) -> None:
|
||||
# Note, if the run_time of AnimationGroup has been
|
||||
|
|
@ -144,14 +165,34 @@ class AnimationGroup(Animation):
|
|||
# times might not correspond to actual times,
|
||||
# e.g. of the surrounding scene. Instead they'd
|
||||
# be a rescaled version. But that's okay!
|
||||
time = self.rate_func(alpha) * self.max_end_time
|
||||
for anim, start_time, end_time in self.anims_with_timings:
|
||||
anim_time = end_time - start_time
|
||||
if anim_time == 0:
|
||||
sub_alpha = 0
|
||||
else:
|
||||
sub_alpha = np.clip((time - start_time) / anim_time, 0, 1)
|
||||
anim.interpolate(sub_alpha)
|
||||
anim_group_time = self.rate_func(alpha) * self.max_end_time
|
||||
time_goes_back = anim_group_time < self.anim_group_time
|
||||
|
||||
# Only update ongoing animations
|
||||
awt = self.anims_with_timings
|
||||
new_begun = anim_group_time >= awt["start"]
|
||||
new_finished = anim_group_time > awt["end"]
|
||||
to_update = awt[
|
||||
(self.anims_begun | new_begun) & (~self.anims_finished | ~new_finished)
|
||||
]
|
||||
|
||||
run_times = to_update["end"] - to_update["start"]
|
||||
with_zero_run_time = run_times == 0
|
||||
run_times[with_zero_run_time] = 1
|
||||
sub_alphas = (anim_group_time - to_update["start"]) / run_times
|
||||
if time_goes_back:
|
||||
sub_alphas[(sub_alphas < 0) | with_zero_run_time] = 0
|
||||
else:
|
||||
sub_alphas[(sub_alphas > 1) | with_zero_run_time] = 1
|
||||
|
||||
for anim_to_update, sub_alpha in zip(
|
||||
to_update["anim"], sub_alphas, strict=True
|
||||
):
|
||||
anim_to_update.interpolate(sub_alpha)
|
||||
|
||||
self.anim_group_time = anim_group_time
|
||||
self.anims_begun = new_begun
|
||||
self.anims_finished = new_finished
|
||||
|
||||
|
||||
class Succession(AnimationGroup):
|
||||
|
|
@ -191,11 +232,15 @@ class Succession(AnimationGroup):
|
|||
))
|
||||
"""
|
||||
|
||||
def __init__(self, *animations: Animation, lag_ratio: float = 1, **kwargs) -> None:
|
||||
def __init__(self, *animations: Animation, lag_ratio: float = 1, **kwargs: Any):
|
||||
super().__init__(*animations, lag_ratio=lag_ratio, **kwargs)
|
||||
|
||||
def begin(self) -> None:
|
||||
assert len(self.animations) > 0
|
||||
if not self.animations:
|
||||
raise ValueError(
|
||||
f"Trying to play {self} without animations, this is not supported. "
|
||||
"Please add at least one subanimation."
|
||||
)
|
||||
self.update_active_animation(0)
|
||||
|
||||
def finish(self) -> None:
|
||||
|
|
@ -206,7 +251,7 @@ class Succession(AnimationGroup):
|
|||
if self.active_animation:
|
||||
self.active_animation.update_mobjects(dt)
|
||||
|
||||
def _setup_scene(self, scene) -> None:
|
||||
def _setup_scene(self, scene: Scene | None) -> None:
|
||||
if scene is None:
|
||||
return
|
||||
if self.is_introducer():
|
||||
|
|
@ -226,8 +271,8 @@ class Succession(AnimationGroup):
|
|||
self.active_animation = self.animations[index]
|
||||
self.active_animation._setup_scene(self.scene)
|
||||
self.active_animation.begin()
|
||||
self.active_start_time = self.anims_with_timings[index][1]
|
||||
self.active_end_time = self.anims_with_timings[index][2]
|
||||
self.active_start_time = self.anims_with_timings[index]["start"]
|
||||
self.active_end_time = self.anims_with_timings[index]["end"]
|
||||
|
||||
def next_animation(self) -> None:
|
||||
"""Proceeds to the next animation.
|
||||
|
|
@ -244,7 +289,7 @@ class Succession(AnimationGroup):
|
|||
self.next_animation()
|
||||
if self.active_animation is not None and self.active_start_time is not None:
|
||||
elapsed = current_time - self.active_start_time
|
||||
active_run_time = self.active_animation.get_run_time()
|
||||
active_run_time = self.active_animation.run_time
|
||||
subalpha = elapsed / active_run_time if active_run_time != 0.0 else 1.0
|
||||
self.active_animation.interpolate(subalpha)
|
||||
|
||||
|
|
@ -298,7 +343,7 @@ class LaggedStart(AnimationGroup):
|
|||
self,
|
||||
*animations: Animation,
|
||||
lag_ratio: float = DEFAULT_LAGGED_START_LAG_RATIO,
|
||||
**kwargs,
|
||||
**kwargs: Any,
|
||||
):
|
||||
super().__init__(*animations, lag_ratio=lag_ratio, **kwargs)
|
||||
|
||||
|
|
@ -308,7 +353,7 @@ class LaggedStartMap(LaggedStart):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
AnimationClass
|
||||
animation_class
|
||||
:class:`~.Animation` to apply to mobject.
|
||||
mobject
|
||||
:class:`~.Mobject` whose submobjects the animation, and optionally the function,
|
||||
|
|
@ -317,6 +362,17 @@ class LaggedStartMap(LaggedStart):
|
|||
Function which will be applied to :class:`~.Mobject`.
|
||||
run_time
|
||||
The duration of the animation in seconds.
|
||||
lag_ratio
|
||||
Defines the delay after which the animation is applied to submobjects. A lag_ratio of
|
||||
``n.nn`` means the next animation will play when ``nnn%`` of the current animation has played.
|
||||
Defaults to 0.05, meaning that the next animation will begin when 5% of the current
|
||||
animation has played.
|
||||
|
||||
This does not influence the total runtime of the animation. Instead the runtime
|
||||
of individual animations is adjusted so that the complete animation has the defined
|
||||
run time.
|
||||
kwargs
|
||||
Further keyword arguments that are passed to `animation_class`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
|
@ -343,20 +399,23 @@ class LaggedStartMap(LaggedStart):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
AnimationClass: Callable[..., Animation],
|
||||
animation_class: type[Animation],
|
||||
mobject: Mobject,
|
||||
arg_creator: Callable[[Mobject], str] = None,
|
||||
arg_creator: Callable[[Mobject], Iterable[Any]] | None = None,
|
||||
run_time: float = 2,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
args_list = []
|
||||
for submob in mobject:
|
||||
if arg_creator:
|
||||
args_list.append(arg_creator(submob))
|
||||
else:
|
||||
args_list.append((submob,))
|
||||
lag_ratio: float = DEFAULT_LAGGED_START_LAG_RATIO,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if arg_creator is None:
|
||||
|
||||
def identity(mob: Mobject) -> Mobject:
|
||||
return mob
|
||||
|
||||
arg_creator = identity
|
||||
|
||||
args_list = [arg_creator(submob) for submob in mobject]
|
||||
anim_kwargs = dict(kwargs)
|
||||
if "lag_ratio" in anim_kwargs:
|
||||
anim_kwargs.pop("lag_ratio")
|
||||
animations = [AnimationClass(*args, **anim_kwargs) for args in args_list]
|
||||
super().__init__(*animations, run_time=run_time, **kwargs)
|
||||
animations = [animation_class(*args, **anim_kwargs) for args in args_list]
|
||||
super().__init__(*animations, run_time=run_time, lag_ratio=lag_ratio)
|
||||
|
|
|
|||
|
|
@ -70,17 +70,22 @@ __all__ = [
|
|||
"RemoveTextLetterByLetter",
|
||||
"ShowSubmobjectsOneByOne",
|
||||
"AddTextWordByWord",
|
||||
"TypeWithCursor",
|
||||
"UntypeWithCursor",
|
||||
]
|
||||
|
||||
|
||||
import itertools as it
|
||||
from typing import TYPE_CHECKING, Callable, Iterable, Sequence
|
||||
from collections.abc import Callable, Iterable, Sequence
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.mobject.text.text_mobject import Text
|
||||
from manim.scene.scene import Scene
|
||||
|
||||
from manim.constants import RIGHT, TAU
|
||||
from manim.mobject.opengl.opengl_surface import OpenGLSurface
|
||||
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
|
||||
from manim.utils.color import ManimColor
|
||||
|
|
@ -88,7 +93,6 @@ from manim.utils.color import ManimColor
|
|||
from .. import config
|
||||
from ..animation.animation import Animation
|
||||
from ..animation.composition import Succession
|
||||
from ..constants import TAU
|
||||
from ..mobject.mobject import Group, Mobject
|
||||
from ..mobject.types.vectorized_mobject import VMobject
|
||||
from ..utils.bezier import integer_interpolate
|
||||
|
|
@ -116,7 +120,7 @@ class ShowPartial(Animation):
|
|||
):
|
||||
pointwise = getattr(mobject, "pointwise_become_partial", None)
|
||||
if not callable(pointwise):
|
||||
raise NotImplementedError("This animation is not defined for this Mobject.")
|
||||
raise TypeError(f"{self.__class__.__name__} only works for VMobjects.")
|
||||
super().__init__(mobject, **kwargs)
|
||||
|
||||
def interpolate_submobject(
|
||||
|
|
@ -129,7 +133,7 @@ class ShowPartial(Animation):
|
|||
starting_submobject, *self._get_bounds(alpha)
|
||||
)
|
||||
|
||||
def _get_bounds(self, alpha: float) -> None:
|
||||
def _get_bounds(self, alpha: float) -> tuple[float, float]:
|
||||
raise NotImplementedError("Please use Create or ShowPassingFlash")
|
||||
|
||||
|
||||
|
|
@ -169,7 +173,7 @@ class Create(ShowPartial):
|
|||
) -> None:
|
||||
super().__init__(mobject, lag_ratio=lag_ratio, introducer=introducer, **kwargs)
|
||||
|
||||
def _get_bounds(self, alpha: float) -> tuple[int, float]:
|
||||
def _get_bounds(self, alpha: float) -> tuple[float, float]:
|
||||
return (0, alpha)
|
||||
|
||||
|
||||
|
|
@ -225,8 +229,6 @@ class DrawBorderThenFill(Animation):
|
|||
rate_func: Callable[[float], float] = double_smooth,
|
||||
stroke_width: float = 2,
|
||||
stroke_color: str = None,
|
||||
draw_border_animation_config: dict = {}, # what does this dict accept?
|
||||
fill_animation_config: dict = {},
|
||||
introducer: bool = True,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
|
|
@ -240,13 +242,13 @@ class DrawBorderThenFill(Animation):
|
|||
)
|
||||
self.stroke_width = stroke_width
|
||||
self.stroke_color = stroke_color
|
||||
self.draw_border_animation_config = draw_border_animation_config
|
||||
self.fill_animation_config = fill_animation_config
|
||||
self.outline = self.get_outline()
|
||||
|
||||
def _typecheck_input(self, vmobject: VMobject | OpenGLVMobject) -> None:
|
||||
if not isinstance(vmobject, (VMobject, OpenGLVMobject)):
|
||||
raise TypeError("DrawBorderThenFill only works for vectorized Mobjects")
|
||||
raise TypeError(
|
||||
f"{self.__class__.__name__} only works for vectorized Mobjects"
|
||||
)
|
||||
|
||||
def begin(self) -> None:
|
||||
self.outline = self.get_outline()
|
||||
|
|
@ -277,7 +279,7 @@ class DrawBorderThenFill(Animation):
|
|||
alpha: float,
|
||||
) -> None: # Fixme: not matching the parent class? What is outline doing here?
|
||||
index: int
|
||||
subalpha: int
|
||||
subalpha: float
|
||||
index, subalpha = integer_interpolate(0, 2, alpha)
|
||||
if index == 0:
|
||||
submobject.pointwise_become_partial(outline, 0, subalpha)
|
||||
|
|
@ -347,10 +349,7 @@ class Write(DrawBorderThenFill):
|
|||
) -> tuple[float, float]:
|
||||
length = len(vmobject.family_members_with_points())
|
||||
if run_time is None:
|
||||
if length < 15:
|
||||
run_time = 1
|
||||
else:
|
||||
run_time = 2
|
||||
run_time = 1 if length < 15 else 2
|
||||
if lag_ratio is None:
|
||||
lag_ratio = min(4.0 / max(1.0, length), 0.2)
|
||||
return run_time, lag_ratio
|
||||
|
|
@ -456,7 +455,7 @@ class SpiralIn(Animation):
|
|||
fade_in_fraction=0.3,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.shapes = shapes
|
||||
self.shapes = shapes.copy()
|
||||
self.scale_factor = scale_factor
|
||||
self.shape_center = shapes.get_center()
|
||||
self.fade_in_fraction = fade_in_fraction
|
||||
|
|
@ -473,15 +472,21 @@ class SpiralIn(Animation):
|
|||
|
||||
def interpolate_mobject(self, alpha: float) -> None:
|
||||
alpha = self.rate_func(alpha)
|
||||
for shape in self.shapes:
|
||||
for original_shape, shape in zip(self.shapes, self.mobject, strict=True):
|
||||
shape.restore()
|
||||
shape.save_state()
|
||||
opacity = shape.get_fill_opacity()
|
||||
new_opacity = min(opacity, alpha * opacity / self.fade_in_fraction)
|
||||
fill_opacity = original_shape.get_fill_opacity()
|
||||
stroke_opacity = original_shape.get_stroke_opacity()
|
||||
new_fill_opacity = min(
|
||||
fill_opacity, alpha * fill_opacity / self.fade_in_fraction
|
||||
)
|
||||
new_stroke_opacity = min(
|
||||
stroke_opacity, alpha * stroke_opacity / self.fade_in_fraction
|
||||
)
|
||||
shape.shift((shape.final_position - shape.initial_position) * alpha)
|
||||
shape.rotate(TAU * alpha, about_point=self.shape_center)
|
||||
shape.rotate(-TAU * alpha, about_point=shape.get_center_of_mass())
|
||||
shape.set_opacity(new_opacity)
|
||||
shape.set_fill(opacity=new_fill_opacity)
|
||||
shape.set_stroke(opacity=new_stroke_opacity)
|
||||
|
||||
|
||||
class ShowIncreasingSubsets(Animation):
|
||||
|
|
@ -668,3 +673,176 @@ class AddTextWordByWord(Succession):
|
|||
)
|
||||
)
|
||||
super().__init__(*anims, **kwargs)
|
||||
|
||||
|
||||
class TypeWithCursor(AddTextLetterByLetter):
|
||||
"""Similar to :class:`~.AddTextLetterByLetter` , but with an additional cursor mobject at the end.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
time_per_char
|
||||
Frequency of appearance of the letters.
|
||||
cursor
|
||||
:class:`~.Mobject` shown after the last added letter.
|
||||
buff
|
||||
Controls how far away the cursor is to the right of the last added letter.
|
||||
keep_cursor_y
|
||||
If ``True``, the cursor's y-coordinate is set to the center of the ``Text`` and remains the same throughout the animation. Otherwise, it is set to the center of the last added letter.
|
||||
leave_cursor_on
|
||||
Whether to show the cursor after the animation.
|
||||
|
||||
.. tip::
|
||||
This is currently only possible for class:`~.Text` and not for class:`~.MathTex`.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: InsertingTextExample
|
||||
:ref_classes: Blink
|
||||
|
||||
class InsertingTextExample(Scene):
|
||||
def construct(self):
|
||||
text = Text("Inserting", color=PURPLE).scale(1.5).to_edge(LEFT)
|
||||
cursor = Rectangle(
|
||||
color = GREY_A,
|
||||
fill_color = GREY_A,
|
||||
fill_opacity = 1.0,
|
||||
height = 1.1,
|
||||
width = 0.5,
|
||||
).move_to(text[0]) # Position the cursor
|
||||
|
||||
self.play(TypeWithCursor(text, cursor))
|
||||
self.play(Blink(cursor, blinks=2))
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
text: Text,
|
||||
cursor: Mobject,
|
||||
buff: float = 0.1,
|
||||
keep_cursor_y: bool = True,
|
||||
leave_cursor_on: bool = True,
|
||||
time_per_char: float = 0.1,
|
||||
reverse_rate_function=False,
|
||||
introducer=True,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.cursor = cursor
|
||||
self.buff = buff
|
||||
self.keep_cursor_y = keep_cursor_y
|
||||
self.leave_cursor_on = leave_cursor_on
|
||||
super().__init__(
|
||||
text,
|
||||
time_per_char=time_per_char,
|
||||
reverse_rate_function=reverse_rate_function,
|
||||
introducer=introducer,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def begin(self) -> None:
|
||||
self.y_cursor = self.cursor.get_y()
|
||||
self.cursor.initial_position = self.mobject.get_center()
|
||||
if self.keep_cursor_y:
|
||||
self.cursor.set_y(self.y_cursor)
|
||||
|
||||
self.cursor.set_opacity(0)
|
||||
self.mobject.add(self.cursor)
|
||||
super().begin()
|
||||
|
||||
def finish(self) -> None:
|
||||
if self.leave_cursor_on:
|
||||
self.cursor.set_opacity(1)
|
||||
else:
|
||||
self.cursor.set_opacity(0)
|
||||
self.mobject.remove(self.cursor)
|
||||
super().finish()
|
||||
|
||||
def clean_up_from_scene(self, scene: Scene) -> None:
|
||||
if not self.leave_cursor_on:
|
||||
scene.remove(self.cursor)
|
||||
super().clean_up_from_scene(scene)
|
||||
|
||||
def update_submobject_list(self, index: int) -> None:
|
||||
for mobj in self.all_submobs[:index]:
|
||||
mobj.set_opacity(1)
|
||||
|
||||
for mobj in self.all_submobs[index:]:
|
||||
mobj.set_opacity(0)
|
||||
|
||||
if index != 0:
|
||||
self.cursor.next_to(
|
||||
self.all_submobs[index - 1], RIGHT, buff=self.buff
|
||||
).set_y(self.cursor.initial_position[1])
|
||||
else:
|
||||
self.cursor.move_to(self.all_submobs[0]).set_y(
|
||||
self.cursor.initial_position[1]
|
||||
)
|
||||
|
||||
if self.keep_cursor_y:
|
||||
self.cursor.set_y(self.y_cursor)
|
||||
self.cursor.set_opacity(1)
|
||||
|
||||
|
||||
class UntypeWithCursor(TypeWithCursor):
|
||||
"""Similar to :class:`~.RemoveTextLetterByLetter` , but with an additional cursor mobject at the end.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
time_per_char
|
||||
Frequency of appearance of the letters.
|
||||
cursor
|
||||
:class:`~.Mobject` shown after the last added letter.
|
||||
buff
|
||||
Controls how far away the cursor is to the right of the last added letter.
|
||||
keep_cursor_y
|
||||
If ``True``, the cursor's y-coordinate is set to the center of the ``Text`` and remains the same throughout the animation. Otherwise, it is set to the center of the last added letter.
|
||||
leave_cursor_on
|
||||
Whether to show the cursor after the animation.
|
||||
|
||||
.. tip::
|
||||
This is currently only possible for class:`~.Text` and not for class:`~.MathTex`.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: DeletingTextExample
|
||||
:ref_classes: Blink
|
||||
|
||||
class DeletingTextExample(Scene):
|
||||
def construct(self):
|
||||
text = Text("Deleting", color=PURPLE).scale(1.5).to_edge(LEFT)
|
||||
cursor = Rectangle(
|
||||
color = GREY_A,
|
||||
fill_color = GREY_A,
|
||||
fill_opacity = 1.0,
|
||||
height = 1.1,
|
||||
width = 0.5,
|
||||
).move_to(text[0]) # Position the cursor
|
||||
|
||||
self.play(UntypeWithCursor(text, cursor))
|
||||
self.play(Blink(cursor, blinks=2))
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
text: Text,
|
||||
cursor: VMobject | None = None,
|
||||
time_per_char: float = 0.1,
|
||||
reverse_rate_function=True,
|
||||
introducer=False,
|
||||
remover=True,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
text,
|
||||
cursor=cursor,
|
||||
time_per_char=time_per_char,
|
||||
reverse_rate_function=reverse_rate_function,
|
||||
introducer=introducer,
|
||||
remover=remover,
|
||||
**kwargs,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -20,6 +19,8 @@ __all__ = [
|
|||
"FadeIn",
|
||||
]
|
||||
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
||||
|
|
@ -54,14 +55,11 @@ class _Fade(Transform):
|
|||
shift: np.ndarray | None = None,
|
||||
target_position: np.ndarray | Mobject | None = None,
|
||||
scale: float = 1,
|
||||
**kwargs,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
if not mobjects:
|
||||
raise ValueError("At least one mobject must be passed.")
|
||||
if len(mobjects) == 1:
|
||||
mobject = mobjects[0]
|
||||
else:
|
||||
mobject = Group(*mobjects)
|
||||
mobject = mobjects[0] if len(mobjects) == 1 else Group(*mobjects)
|
||||
|
||||
self.point_target = False
|
||||
if shift is None:
|
||||
|
|
@ -89,7 +87,7 @@ class _Fade(Transform):
|
|||
Mobject
|
||||
The faded, shifted and scaled copy of the mobject.
|
||||
"""
|
||||
faded_mobject = self.mobject.copy()
|
||||
faded_mobject: Mobject = self.mobject.copy() # type: ignore[assignment]
|
||||
faded_mobject.fade(1)
|
||||
direction_modifier = -1 if fadeIn and not self.point_target else 1
|
||||
faded_mobject.shift(self.shift_vector * direction_modifier)
|
||||
|
|
@ -98,7 +96,7 @@ class _Fade(Transform):
|
|||
|
||||
|
||||
class FadeIn(_Fade):
|
||||
"""Fade in :class:`~.Mobject` s.
|
||||
r"""Fade in :class:`~.Mobject` s.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -123,7 +121,7 @@ class FadeIn(_Fade):
|
|||
dot = Dot(UP * 2 + LEFT)
|
||||
self.add(dot)
|
||||
tex = Tex(
|
||||
"FadeIn with ", "shift ", " or target\\_position", " and scale"
|
||||
"FadeIn with ", "shift ", r" or target\_position", " and scale"
|
||||
).scale(1)
|
||||
animations = [
|
||||
FadeIn(tex[0]),
|
||||
|
|
@ -135,18 +133,18 @@ class FadeIn(_Fade):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, *mobjects: Mobject, **kwargs) -> None:
|
||||
def __init__(self, *mobjects: Mobject, **kwargs: Any) -> None:
|
||||
super().__init__(*mobjects, introducer=True, **kwargs)
|
||||
|
||||
def create_target(self):
|
||||
return self.mobject
|
||||
def create_target(self) -> Mobject:
|
||||
return self.mobject # type: ignore[return-value]
|
||||
|
||||
def create_starting_mobject(self):
|
||||
def create_starting_mobject(self) -> Mobject:
|
||||
return self._create_faded_mobject(fadeIn=True)
|
||||
|
||||
|
||||
class FadeOut(_Fade):
|
||||
"""Fade out :class:`~.Mobject` s.
|
||||
r"""Fade out :class:`~.Mobject` s.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -170,7 +168,7 @@ class FadeOut(_Fade):
|
|||
dot = Dot(UP * 2 + LEFT)
|
||||
self.add(dot)
|
||||
tex = Tex(
|
||||
"FadeOut with ", "shift ", " or target\\_position", " and scale"
|
||||
"FadeOut with ", "shift ", r" or target\_position", " and scale"
|
||||
).scale(1)
|
||||
animations = [
|
||||
FadeOut(tex[0]),
|
||||
|
|
@ -183,12 +181,12 @@ class FadeOut(_Fade):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, *mobjects: Mobject, **kwargs) -> None:
|
||||
def __init__(self, *mobjects: Mobject, **kwargs: Any) -> None:
|
||||
super().__init__(*mobjects, remover=True, **kwargs)
|
||||
|
||||
def create_target(self):
|
||||
def create_target(self) -> Mobject:
|
||||
return self._create_faded_mobject(fadeIn=False)
|
||||
|
||||
def clean_up_from_scene(self, scene: Scene = None) -> None:
|
||||
def clean_up_from_scene(self, scene: Scene) -> None:
|
||||
super().clean_up_from_scene(scene)
|
||||
self.interpolate(0)
|
||||
|
|
|
|||
|
|
@ -31,16 +31,17 @@ __all__ = [
|
|||
"SpinInFromNothing",
|
||||
]
|
||||
|
||||
import typing
|
||||
|
||||
import numpy as np
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from ..animation.transform import Transform
|
||||
from ..constants import PI
|
||||
from ..utils.paths import spiral_path
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
if TYPE_CHECKING:
|
||||
from manim.mobject.geometry.line import Arrow
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
||||
from manim.typing import Point3DLike, Vector3DLike
|
||||
from manim.utils.color import ParsableManimColor
|
||||
|
||||
from ..mobject.mobject import Mobject
|
||||
|
||||
|
|
@ -76,16 +77,20 @@ class GrowFromPoint(Transform):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, mobject: Mobject, point: np.ndarray, point_color: str = None, **kwargs
|
||||
) -> None:
|
||||
self,
|
||||
mobject: Mobject,
|
||||
point: Point3DLike,
|
||||
point_color: ParsableManimColor | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.point = point
|
||||
self.point_color = point_color
|
||||
super().__init__(mobject, introducer=True, **kwargs)
|
||||
|
||||
def create_target(self) -> Mobject:
|
||||
def create_target(self) -> Mobject | OpenGLMobject:
|
||||
return self.mobject
|
||||
|
||||
def create_starting_mobject(self) -> Mobject:
|
||||
def create_starting_mobject(self) -> Mobject | OpenGLMobject:
|
||||
start = super().create_starting_mobject()
|
||||
start.scale(0)
|
||||
start.move_to(self.point)
|
||||
|
|
@ -118,7 +123,12 @@ class GrowFromCenter(GrowFromPoint):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, mobject: Mobject, point_color: str = None, **kwargs) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
mobject: Mobject,
|
||||
point_color: ParsableManimColor | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
point = mobject.get_center()
|
||||
super().__init__(mobject, point, point_color=point_color, **kwargs)
|
||||
|
||||
|
|
@ -153,8 +163,12 @@ class GrowFromEdge(GrowFromPoint):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, mobject: Mobject, edge: np.ndarray, point_color: str = None, **kwargs
|
||||
) -> None:
|
||||
self,
|
||||
mobject: Mobject,
|
||||
edge: Vector3DLike,
|
||||
point_color: ParsableManimColor | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
point = mobject.get_critical_point(edge)
|
||||
super().__init__(mobject, point, point_color=point_color, **kwargs)
|
||||
|
||||
|
|
@ -183,11 +197,13 @@ class GrowArrow(GrowFromPoint):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, arrow: Arrow, point_color: str = None, **kwargs) -> None:
|
||||
def __init__(
|
||||
self, arrow: Arrow, point_color: ParsableManimColor | None = None, **kwargs: Any
|
||||
):
|
||||
point = arrow.get_start()
|
||||
super().__init__(arrow, point, point_color=point_color, **kwargs)
|
||||
|
||||
def create_starting_mobject(self) -> Mobject:
|
||||
def create_starting_mobject(self) -> Mobject | OpenGLMobject:
|
||||
start_arrow = self.mobject.copy()
|
||||
start_arrow.scale(0, scale_tips=True, about_point=self.point)
|
||||
if self.point_color:
|
||||
|
|
@ -224,8 +240,12 @@ class SpinInFromNothing(GrowFromCenter):
|
|||
"""
|
||||
|
||||
def __init__(
|
||||
self, mobject: Mobject, angle: float = PI / 2, point_color: str = None, **kwargs
|
||||
) -> None:
|
||||
self,
|
||||
mobject: Mobject,
|
||||
angle: float = PI / 2,
|
||||
point_color: ParsableManimColor | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.angle = angle
|
||||
super().__init__(
|
||||
mobject, path_func=spiral_path(angle), point_color=point_color, **kwargs
|
||||
|
|
|
|||
|
|
@ -25,19 +25,22 @@ Examples
|
|||
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = [
|
||||
"FocusOn",
|
||||
"Indicate",
|
||||
"Flash",
|
||||
"ShowPassingFlash",
|
||||
"ShowPassingFlashWithThinningStrokeWidth",
|
||||
"ShowCreationThenFadeOut",
|
||||
"ApplyWave",
|
||||
"Circumscribe",
|
||||
"Wiggle",
|
||||
"Blink",
|
||||
]
|
||||
|
||||
from typing import Callable, Iterable, Optional, Tuple, Type, Union
|
||||
from collections.abc import Iterable
|
||||
from typing import Any, Self
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -45,6 +48,7 @@ from manim.mobject.geometry.arc import Circle, Dot
|
|||
from manim.mobject.geometry.line import Line
|
||||
from manim.mobject.geometry.polygram import Rectangle
|
||||
from manim.mobject.geometry.shape_matchers import SurroundingRectangle
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
||||
from manim.scene.scene import Scene
|
||||
|
||||
from .. import config
|
||||
|
|
@ -54,13 +58,14 @@ from ..animation.creation import Create, ShowPartial, Uncreate
|
|||
from ..animation.fading import FadeIn, FadeOut
|
||||
from ..animation.movement import Homotopy
|
||||
from ..animation.transform import Transform
|
||||
from ..animation.updaters.update import UpdateFromFunc
|
||||
from ..constants import *
|
||||
from ..mobject.mobject import Mobject
|
||||
from ..mobject.types.vectorized_mobject import VGroup, VMobject
|
||||
from ..typing import Point3D, Point3DLike, Vector3DLike
|
||||
from ..utils.bezier import interpolate, inverse_interpolate
|
||||
from ..utils.color import GREY, YELLOW, ParsableManimColor
|
||||
from ..utils.deprecation import deprecated
|
||||
from ..utils.rate_functions import smooth, there_and_back, wiggle
|
||||
from ..utils.color import GREY, PURE_YELLOW, ParsableManimColor
|
||||
from ..utils.rate_functions import RateFunction, smooth, there_and_back, wiggle
|
||||
from ..utils.space_ops import normalize
|
||||
|
||||
|
||||
|
|
@ -77,8 +82,6 @@ class FocusOn(Transform):
|
|||
The color of the spotlight.
|
||||
run_time
|
||||
The duration of the animation.
|
||||
kwargs
|
||||
Additional arguments to be passed to the :class:`~.Succession` constructor
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
|
@ -86,7 +89,7 @@ class FocusOn(Transform):
|
|||
|
||||
class UsingFocusOn(Scene):
|
||||
def construct(self):
|
||||
dot = Dot(color=YELLOW).shift(DOWN)
|
||||
dot = Dot(color=PURE_YELLOW).shift(DOWN)
|
||||
self.add(Tex("Focusing on the dot below:"), dot)
|
||||
self.play(FocusOn(dot))
|
||||
self.wait()
|
||||
|
|
@ -94,12 +97,12 @@ class FocusOn(Transform):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
focus_point: Union[np.ndarray, Mobject],
|
||||
focus_point: Point3DLike | Mobject,
|
||||
opacity: float = 0.2,
|
||||
color: str = GREY,
|
||||
color: ParsableManimColor = GREY,
|
||||
run_time: float = 2,
|
||||
**kwargs
|
||||
) -> None:
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.focus_point = focus_point
|
||||
self.color = color
|
||||
self.opacity = opacity
|
||||
|
|
@ -131,7 +134,7 @@ class Indicate(Transform):
|
|||
color
|
||||
The color the mobject temporally takes.
|
||||
rate_func
|
||||
The function definig the animation progress at every point in time.
|
||||
The function defining the animation progress at every point in time.
|
||||
kwargs
|
||||
Additional arguments to be passed to the :class:`~.Succession` constructor
|
||||
|
||||
|
|
@ -148,17 +151,17 @@ class Indicate(Transform):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: "Mobject",
|
||||
mobject: Mobject,
|
||||
scale_factor: float = 1.2,
|
||||
color: str = YELLOW,
|
||||
rate_func: Callable[[float, Optional[float]], np.ndarray] = there_and_back,
|
||||
**kwargs
|
||||
) -> None:
|
||||
color: ParsableManimColor = PURE_YELLOW,
|
||||
rate_func: RateFunction = there_and_back,
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.color = color
|
||||
self.scale_factor = scale_factor
|
||||
super().__init__(mobject, rate_func=rate_func, **kwargs)
|
||||
|
||||
def create_target(self) -> "Mobject":
|
||||
def create_target(self) -> Mobject | OpenGLMobject:
|
||||
target = self.mobject.copy()
|
||||
target.scale(self.scale_factor)
|
||||
target.set_color(self.color)
|
||||
|
|
@ -195,7 +198,7 @@ class Flash(AnimationGroup):
|
|||
|
||||
class UsingFlash(Scene):
|
||||
def construct(self):
|
||||
dot = Dot(color=YELLOW).shift(DOWN)
|
||||
dot = Dot(color=PURE_YELLOW).shift(DOWN)
|
||||
self.add(Tex("Flash the dot below:"), dot)
|
||||
self.play(Flash(dot))
|
||||
self.wait()
|
||||
|
|
@ -218,20 +221,20 @@ class Flash(AnimationGroup):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
point: Union[np.ndarray, Mobject],
|
||||
point: Point3DLike | Mobject,
|
||||
line_length: float = 0.2,
|
||||
num_lines: int = 12,
|
||||
flash_radius: float = 0.1,
|
||||
line_stroke_width: int = 3,
|
||||
color: str = YELLOW,
|
||||
color: ParsableManimColor = PURE_YELLOW,
|
||||
time_width: float = 1,
|
||||
run_time: float = 1.0,
|
||||
**kwargs
|
||||
) -> None:
|
||||
**kwargs: Any,
|
||||
):
|
||||
if isinstance(point, Mobject):
|
||||
self.point = point.get_center()
|
||||
self.point: Point3D = point.get_center()
|
||||
else:
|
||||
self.point = point
|
||||
self.point = np.asarray(point)
|
||||
self.color = color
|
||||
self.line_length = line_length
|
||||
self.num_lines = num_lines
|
||||
|
|
@ -256,7 +259,7 @@ class Flash(AnimationGroup):
|
|||
lines.set_stroke(width=self.line_stroke_width)
|
||||
return lines
|
||||
|
||||
def create_line_anims(self) -> Iterable["ShowPassingFlash"]:
|
||||
def create_line_anims(self) -> Iterable[ShowPassingFlash]:
|
||||
return [
|
||||
ShowPassingFlash(
|
||||
line,
|
||||
|
|
@ -269,7 +272,7 @@ class Flash(AnimationGroup):
|
|||
|
||||
|
||||
class ShowPassingFlash(ShowPartial):
|
||||
"""Show only a sliver of the VMobject each frame.
|
||||
r"""Show only a sliver of the VMobject each frame.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -289,7 +292,7 @@ class ShowPassingFlash(ShowPartial):
|
|||
self.add(p, lbl)
|
||||
p = p.copy().set_color(BLUE)
|
||||
for time_width in [0.2, 0.5, 1, 2]:
|
||||
lbl.become(Tex(r"\\texttt{time\\_width={{%.1f}}}"%time_width))
|
||||
lbl.become(Tex(r"\texttt{time\_width={{%.1f}}}"%time_width))
|
||||
self.play(ShowPassingFlash(
|
||||
p.copy().set_color(BLUE),
|
||||
run_time=2,
|
||||
|
|
@ -302,11 +305,13 @@ class ShowPassingFlash(ShowPartial):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, mobject: "VMobject", time_width: float = 0.1, **kwargs) -> None:
|
||||
def __init__(
|
||||
self, mobject: VMobject, time_width: float = 0.1, **kwargs: Any
|
||||
) -> None:
|
||||
self.time_width = time_width
|
||||
super().__init__(mobject, remover=True, introducer=True, **kwargs)
|
||||
|
||||
def _get_bounds(self, alpha: float) -> Tuple[float]:
|
||||
def _get_bounds(self, alpha: float) -> tuple[float, float]:
|
||||
tw = self.time_width
|
||||
upper = interpolate(0, 1 + tw, alpha)
|
||||
lower = upper - tw
|
||||
|
|
@ -321,7 +326,14 @@ class ShowPassingFlash(ShowPartial):
|
|||
|
||||
|
||||
class ShowPassingFlashWithThinningStrokeWidth(AnimationGroup):
|
||||
def __init__(self, vmobject, n_segments=10, time_width=0.1, remover=True, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
vmobject: VMobject,
|
||||
n_segments: int = 10,
|
||||
time_width: float = 0.1,
|
||||
remover: bool = True,
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.n_segments = n_segments
|
||||
self.time_width = time_width
|
||||
self.remover = remover
|
||||
|
|
@ -337,21 +349,12 @@ class ShowPassingFlashWithThinningStrokeWidth(AnimationGroup):
|
|||
for stroke_width, time_width in zip(
|
||||
np.linspace(0, max_stroke_width, self.n_segments),
|
||||
np.linspace(max_time_width, 0, self.n_segments),
|
||||
strict=True,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@deprecated(
|
||||
since="v0.15.0",
|
||||
until="v0.16.0",
|
||||
message="Use Create then FadeOut to achieve this effect.",
|
||||
)
|
||||
class ShowCreationThenFadeOut(Succession):
|
||||
def __init__(self, mobject: "Mobject", remover: bool = True, **kwargs) -> None:
|
||||
super().__init__(Create(mobject), FadeOut(mobject), remover=remover, **kwargs)
|
||||
|
||||
|
||||
class ApplyWave(Homotopy):
|
||||
"""Send a wave through the Mobject distorting it temporarily.
|
||||
|
||||
|
|
@ -397,25 +400,25 @@ class ApplyWave(Homotopy):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: "Mobject",
|
||||
direction: np.ndarray = UP,
|
||||
mobject: Mobject,
|
||||
direction: Vector3DLike = UP,
|
||||
amplitude: float = 0.2,
|
||||
wave_func: Callable[[float], float] = smooth,
|
||||
wave_func: RateFunction = smooth,
|
||||
time_width: float = 1,
|
||||
ripples: int = 1,
|
||||
run_time: float = 2,
|
||||
**kwargs
|
||||
) -> None:
|
||||
**kwargs: Any,
|
||||
):
|
||||
x_min = mobject.get_left()[0]
|
||||
x_max = mobject.get_right()[0]
|
||||
vect = amplitude * normalize(direction)
|
||||
|
||||
def wave(t):
|
||||
def wave(t: float) -> float:
|
||||
# Creates a wave with n ripples from a simple rate_func
|
||||
# This wave is build up as follows:
|
||||
# The time is split into 2*ripples phases. In every phase the amplitude
|
||||
# either rises to one or goes down to zero. Consecutive ripples will have
|
||||
# their amplitudes in oppising directions (first ripple from 0 to 1 to 0,
|
||||
# their amplitudes in opposing directions (first ripple from 0 to 1 to 0,
|
||||
# second from 0 to -1 to 0 and so on). This is how two ripples would be
|
||||
# divided into phases:
|
||||
|
||||
|
|
@ -454,7 +457,7 @@ class ApplyWave(Homotopy):
|
|||
return wave_func(t * phases)
|
||||
elif phase == phases - 1:
|
||||
# last ripple. Rising or falling depending on the number of ripples
|
||||
# The (ripples % 2)-term is used to make this destinction.
|
||||
# The (ripples % 2)-term is used to make this distinction.
|
||||
t -= phase / phases # Time relative to the phase
|
||||
return (1 - wave_func(t * phases)) * (2 * (ripples % 2) - 1)
|
||||
else:
|
||||
|
|
@ -470,13 +473,14 @@ class ApplyWave(Homotopy):
|
|||
y: float,
|
||||
z: float,
|
||||
t: float,
|
||||
) -> Tuple[float, float, float]:
|
||||
) -> tuple[float, float, float]:
|
||||
upper = interpolate(0, 1 + time_width, t)
|
||||
lower = upper - time_width
|
||||
relative_x = inverse_interpolate(x_min, x_max, x)
|
||||
wave_phase = inverse_interpolate(lower, upper, relative_x)
|
||||
nudge = wave(wave_phase) * vect
|
||||
return np.array([x, y, z]) + nudge
|
||||
return_value: tuple[float, float, float] = np.array([x, y, z]) + nudge
|
||||
return return_value
|
||||
|
||||
super().__init__(homotopy, mobject, run_time=run_time, **kwargs)
|
||||
|
||||
|
|
@ -516,38 +520,42 @@ class Wiggle(Animation):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: "Mobject",
|
||||
mobject: Mobject,
|
||||
scale_value: float = 1.1,
|
||||
rotation_angle: float = 0.01 * TAU,
|
||||
n_wiggles: int = 6,
|
||||
scale_about_point: Optional[np.ndarray] = None,
|
||||
rotate_about_point: Optional[np.ndarray] = None,
|
||||
scale_about_point: Point3DLike | None = None,
|
||||
rotate_about_point: Point3DLike | None = None,
|
||||
run_time: float = 2,
|
||||
**kwargs
|
||||
) -> None:
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.scale_value = scale_value
|
||||
self.rotation_angle = rotation_angle
|
||||
self.n_wiggles = n_wiggles
|
||||
self.scale_about_point = scale_about_point
|
||||
if scale_about_point is not None:
|
||||
self.scale_about_point = np.array(scale_about_point)
|
||||
self.rotate_about_point = rotate_about_point
|
||||
if rotate_about_point is not None:
|
||||
self.rotate_about_point = np.array(rotate_about_point)
|
||||
super().__init__(mobject, run_time=run_time, **kwargs)
|
||||
|
||||
def get_scale_about_point(self) -> np.ndarray:
|
||||
def get_scale_about_point(self) -> Point3D:
|
||||
if self.scale_about_point is None:
|
||||
return self.mobject.get_center()
|
||||
return self.scale_about_point
|
||||
|
||||
def get_rotate_about_point(self) -> np.ndarray:
|
||||
def get_rotate_about_point(self) -> Point3D:
|
||||
if self.rotate_about_point is None:
|
||||
return self.mobject.get_center()
|
||||
return self.rotate_about_point
|
||||
|
||||
def interpolate_submobject(
|
||||
self,
|
||||
submobject: "Mobject",
|
||||
starting_submobject: "Mobject",
|
||||
submobject: Mobject,
|
||||
starting_submobject: Mobject,
|
||||
alpha: float,
|
||||
) -> None:
|
||||
) -> Self:
|
||||
submobject.points[:, :] = starting_submobject.points
|
||||
submobject.scale(
|
||||
interpolate(1, self.scale_value, there_and_back(alpha)),
|
||||
|
|
@ -557,17 +565,18 @@ class Wiggle(Animation):
|
|||
wiggle(alpha, self.n_wiggles) * self.rotation_angle,
|
||||
about_point=self.get_rotate_about_point(),
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class Circumscribe(Succession):
|
||||
"""Draw a temporary line surrounding the mobject.
|
||||
r"""Draw a temporary line surrounding the mobject.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mobject
|
||||
The mobject to be circumscribed.
|
||||
shape
|
||||
The shape with which to surrond the given mobject. Should be either
|
||||
The shape with which to surround the given mobject. Should be either
|
||||
:class:`~.Rectangle` or :class:`~.Circle`
|
||||
fade_in
|
||||
Whether to make the surrounding shape to fade in. It will be drawn otherwise.
|
||||
|
|
@ -591,7 +600,7 @@ class Circumscribe(Succession):
|
|||
|
||||
class UsingCircumscribe(Scene):
|
||||
def construct(self):
|
||||
lbl = Tex(r"Circum-\\\\scribe").scale(2)
|
||||
lbl = Tex(r"Circum-\\scribe").scale(2)
|
||||
self.add(lbl)
|
||||
self.play(Circumscribe(lbl))
|
||||
self.play(Circumscribe(lbl, Circle))
|
||||
|
|
@ -604,21 +613,21 @@ class Circumscribe(Succession):
|
|||
def __init__(
|
||||
self,
|
||||
mobject: Mobject,
|
||||
shape: Type = Rectangle,
|
||||
fade_in=False,
|
||||
fade_out=False,
|
||||
time_width=0.3,
|
||||
shape: type[Rectangle] | type[Circle] = Rectangle,
|
||||
fade_in: bool = False,
|
||||
fade_out: bool = False,
|
||||
time_width: float = 0.3,
|
||||
buff: float = SMALL_BUFF,
|
||||
color: ParsableManimColor = YELLOW,
|
||||
run_time=1,
|
||||
stroke_width=DEFAULT_STROKE_WIDTH,
|
||||
**kwargs
|
||||
color: ParsableManimColor = PURE_YELLOW,
|
||||
run_time: float = 1,
|
||||
stroke_width: float = DEFAULT_STROKE_WIDTH,
|
||||
**kwargs: Any,
|
||||
):
|
||||
if shape is Rectangle:
|
||||
frame = SurroundingRectangle(
|
||||
frame: SurroundingRectangle | Circle = SurroundingRectangle(
|
||||
mobject,
|
||||
color,
|
||||
buff,
|
||||
color=color,
|
||||
buff=buff,
|
||||
stroke_width=stroke_width,
|
||||
)
|
||||
elif shape is Circle:
|
||||
|
|
@ -654,3 +663,68 @@ class Circumscribe(Succession):
|
|||
super().__init__(
|
||||
ShowPassingFlash(frame, time_width, run_time=run_time), **kwargs
|
||||
)
|
||||
|
||||
|
||||
class Blink(Succession):
|
||||
"""Blink the mobject.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mobject
|
||||
The mobject to be blinked.
|
||||
time_on
|
||||
The duration that the mobject is shown for one blink.
|
||||
time_off
|
||||
The duration that the mobject is hidden for one blink.
|
||||
blinks
|
||||
The number of blinks
|
||||
hide_at_end
|
||||
Whether to hide the mobject at the end of the animation.
|
||||
kwargs
|
||||
Additional arguments to be passed to the :class:`~.Succession` constructor.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: BlinkingExample
|
||||
|
||||
class BlinkingExample(Scene):
|
||||
def construct(self):
|
||||
text = Text("Blinking").scale(1.5)
|
||||
self.add(text)
|
||||
self.play(Blink(text, blinks=3))
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: Mobject,
|
||||
time_on: float = 0.5,
|
||||
time_off: float = 0.5,
|
||||
blinks: int = 1,
|
||||
hide_at_end: bool = False,
|
||||
**kwargs: Any,
|
||||
):
|
||||
animations = [
|
||||
UpdateFromFunc(
|
||||
mobject,
|
||||
update_function=lambda mob: mob.set_opacity(1.0),
|
||||
run_time=time_on,
|
||||
),
|
||||
UpdateFromFunc(
|
||||
mobject,
|
||||
update_function=lambda mob: mob.set_opacity(0.0),
|
||||
run_time=time_off,
|
||||
),
|
||||
] * blinks
|
||||
|
||||
if not hide_at_end:
|
||||
animations.append(
|
||||
UpdateFromFunc(
|
||||
mobject,
|
||||
update_function=lambda mob: mob.set_opacity(1.0),
|
||||
run_time=time_on,
|
||||
),
|
||||
)
|
||||
|
||||
super().__init__(*animations, **kwargs)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ __all__ = [
|
|||
"MoveAlongPath",
|
||||
]
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -18,7 +19,13 @@ from ..animation.animation import Animation
|
|||
from ..utils.rate_functions import linear
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..mobject.mobject import Mobject, VMobject
|
||||
from typing import Self
|
||||
|
||||
from manim.mobject.types.vectorized_mobject import VMobject
|
||||
from manim.typing import MappingFunction, Point3D
|
||||
from manim.utils.rate_functions import RateFunction
|
||||
|
||||
from ..mobject.mobject import Mobject
|
||||
|
||||
|
||||
class Homotopy(Animation):
|
||||
|
|
@ -44,6 +51,26 @@ class Homotopy(Animation):
|
|||
Keyword arguments propagated to :meth:`.Mobject.apply_function`.
|
||||
kwargs
|
||||
Further keyword arguments passed to the parent class.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: HomotopyExample
|
||||
|
||||
class HomotopyExample(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
|
||||
def homotopy(x, y, z, t):
|
||||
if t <= 0.25:
|
||||
progress = t / 0.25
|
||||
return (x, y + progress * 0.2 * np.sin(x), z)
|
||||
else:
|
||||
wave_progress = (t - 0.25) / 0.75
|
||||
return (x, y + 0.2 * np.sin(x + 10 * wave_progress), z)
|
||||
|
||||
self.play(Homotopy(homotopy, square, rate_func= linear, run_time=2))
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
|
@ -52,27 +79,33 @@ class Homotopy(Animation):
|
|||
mobject: Mobject,
|
||||
run_time: float = 3,
|
||||
apply_function_kwargs: dict[str, Any] | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.homotopy = homotopy
|
||||
self.apply_function_kwargs = (
|
||||
apply_function_kwargs if apply_function_kwargs is not None else {}
|
||||
)
|
||||
super().__init__(mobject, run_time=run_time, **kwargs)
|
||||
|
||||
def function_at_time_t(self, t: float) -> tuple[float, float, float]:
|
||||
return lambda p: self.homotopy(*p, t)
|
||||
def function_at_time_t(self, t: float) -> MappingFunction:
|
||||
def mapping_function(p: Point3D) -> Point3D:
|
||||
x, y, z = p
|
||||
return np.array(self.homotopy(x, y, z, t))
|
||||
|
||||
return mapping_function
|
||||
|
||||
def interpolate_submobject(
|
||||
self,
|
||||
submobject: Mobject,
|
||||
starting_submobject: Mobject,
|
||||
alpha: float,
|
||||
) -> None:
|
||||
) -> Self:
|
||||
submobject.points = starting_submobject.points
|
||||
submobject.apply_function(
|
||||
self.function_at_time_t(alpha), **self.apply_function_kwargs
|
||||
self.function_at_time_t(alpha),
|
||||
**self.apply_function_kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class SmoothedVectorizedHomotopy(Homotopy):
|
||||
|
|
@ -81,18 +114,21 @@ class SmoothedVectorizedHomotopy(Homotopy):
|
|||
submobject: Mobject,
|
||||
starting_submobject: Mobject,
|
||||
alpha: float,
|
||||
) -> None:
|
||||
) -> Self:
|
||||
assert isinstance(submobject, VMobject)
|
||||
super().interpolate_submobject(submobject, starting_submobject, alpha)
|
||||
submobject.make_smooth()
|
||||
return self
|
||||
|
||||
|
||||
class ComplexHomotopy(Homotopy):
|
||||
def __init__(
|
||||
self, complex_homotopy: Callable[[complex], float], mobject: Mobject, **kwargs
|
||||
) -> None:
|
||||
"""
|
||||
Complex Homotopy a function Cx[0, 1] to C
|
||||
"""
|
||||
self,
|
||||
complex_homotopy: Callable[[complex, float], float],
|
||||
mobject: Mobject,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""Complex Homotopy a function Cx[0, 1] to C"""
|
||||
|
||||
def homotopy(
|
||||
x: float,
|
||||
|
|
@ -113,9 +149,9 @@ class PhaseFlow(Animation):
|
|||
mobject: Mobject,
|
||||
virtual_time: float = 1,
|
||||
suspend_mobject_updating: bool = False,
|
||||
rate_func: Callable[[float], float] = linear,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
rate_func: RateFunction = linear,
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.virtual_time = virtual_time
|
||||
self.function = function
|
||||
super().__init__(
|
||||
|
|
@ -131,13 +167,12 @@ class PhaseFlow(Animation):
|
|||
self.rate_func(alpha) - self.rate_func(self.last_alpha)
|
||||
)
|
||||
self.mobject.apply_function(lambda p: p + dt * self.function(p))
|
||||
self.last_alpha = alpha
|
||||
self.last_alpha: float = alpha
|
||||
|
||||
|
||||
class MoveAlongPath(Animation):
|
||||
"""Make one mobject move along the path of another mobject.
|
||||
Example
|
||||
--------
|
||||
|
||||
.. manim:: MoveAlongPathExample
|
||||
|
||||
class MoveAlongPathExample(Scene):
|
||||
|
|
@ -154,9 +189,9 @@ class MoveAlongPath(Animation):
|
|||
self,
|
||||
mobject: Mobject,
|
||||
path: VMobject,
|
||||
suspend_mobject_updating: bool | None = False,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
suspend_mobject_updating: bool = False,
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.path = path
|
||||
super().__init__(
|
||||
mobject, suspend_mobject_updating=suspend_mobject_updating, **kwargs
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ from __future__ import annotations
|
|||
__all__ = ["ChangingDecimal", "ChangeDecimalToValue"]
|
||||
|
||||
|
||||
import typing
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from manim.mobject.text.numbers import DecimalNumber
|
||||
|
||||
|
|
@ -14,12 +15,47 @@ from ..utils.bezier import interpolate
|
|||
|
||||
|
||||
class ChangingDecimal(Animation):
|
||||
"""Animate a :class:`~.DecimalNumber` to values specified by a user-supplied function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
decimal_mob
|
||||
The :class:`~.DecimalNumber` instance to animate.
|
||||
number_update_func
|
||||
A function that returns the number to display at each point in the animation.
|
||||
suspend_mobject_updating
|
||||
If ``True``, the mobject is not updated outside this animation.
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError
|
||||
If ``decimal_mob`` is not an instance of :class:`~.DecimalNumber`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: ChangingDecimalExample
|
||||
|
||||
class ChangingDecimalExample(Scene):
|
||||
def construct(self):
|
||||
number = DecimalNumber(0)
|
||||
self.add(number)
|
||||
self.play(
|
||||
ChangingDecimal(
|
||||
number,
|
||||
lambda a: 5 * a,
|
||||
run_time=3
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
decimal_mob: DecimalNumber,
|
||||
number_update_func: typing.Callable[[float], float],
|
||||
suspend_mobject_updating: bool | None = False,
|
||||
**kwargs,
|
||||
number_update_func: Callable[[float], float],
|
||||
suspend_mobject_updating: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
self.check_validity_of_input(decimal_mob)
|
||||
self.number_update_func = number_update_func
|
||||
|
|
@ -32,12 +68,34 @@ class ChangingDecimal(Animation):
|
|||
raise TypeError("ChangingDecimal can only take in a DecimalNumber")
|
||||
|
||||
def interpolate_mobject(self, alpha: float) -> None:
|
||||
self.mobject.set_value(self.number_update_func(self.rate_func(alpha)))
|
||||
self.mobject.set_value(self.number_update_func(self.rate_func(alpha))) # type: ignore[attr-defined]
|
||||
|
||||
|
||||
class ChangeDecimalToValue(ChangingDecimal):
|
||||
"""Animate a :class:`~.DecimalNumber` to a target value using linear interpolation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
decimal_mob
|
||||
The :class:`~.DecimalNumber` instance to animate.
|
||||
target_number
|
||||
The target value to transition to.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. manim:: ChangeDecimalToValueExample
|
||||
|
||||
class ChangeDecimalToValueExample(Scene):
|
||||
def construct(self):
|
||||
number = DecimalNumber(0)
|
||||
self.add(number)
|
||||
self.play(ChangeDecimalToValue(number, 10, run_time=3))
|
||||
self.wait()
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, decimal_mob: DecimalNumber, target_number: int, **kwargs
|
||||
self, decimal_mob: DecimalNumber, target_number: int, **kwargs: Any
|
||||
) -> None:
|
||||
start_number = decimal_mob.number
|
||||
super().__init__(
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ from __future__ import annotations
|
|||
|
||||
__all__ = ["Rotating", "Rotate"]
|
||||
|
||||
from typing import TYPE_CHECKING, Callable, Sequence
|
||||
|
||||
import numpy as np
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from ..animation.animation import Animation
|
||||
from ..animation.transform import Transform
|
||||
|
|
@ -15,22 +14,90 @@ from ..utils.rate_functions import linear
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from ..mobject.mobject import Mobject
|
||||
from ..mobject.opengl.opengl_mobject import OpenGLMobject
|
||||
from ..typing import Point3DLike, Vector3DLike
|
||||
|
||||
|
||||
class Rotating(Animation):
|
||||
"""Animation that rotates a Mobject.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mobject
|
||||
The mobject to be rotated.
|
||||
angle
|
||||
The rotation angle in radians. Predefined constants such as ``DEGREES``
|
||||
can also be used to specify the angle in degrees.
|
||||
axis
|
||||
The rotation axis as a numpy vector.
|
||||
about_point
|
||||
The rotation center.
|
||||
about_edge
|
||||
If ``about_point`` is ``None``, this argument specifies
|
||||
the direction of the bounding box point to be taken as
|
||||
the rotation center.
|
||||
run_time
|
||||
The duration of the animation in seconds.
|
||||
rate_func
|
||||
The function defining the animation progress based on the relative
|
||||
runtime (see :mod:`~.rate_functions`) .
|
||||
**kwargs
|
||||
Additional keyword arguments passed to :class:`~.Animation`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
.. manim:: RotatingDemo
|
||||
|
||||
class RotatingDemo(Scene):
|
||||
def construct(self):
|
||||
circle = Circle(radius=1, color=BLUE)
|
||||
line = Line(start=ORIGIN, end=RIGHT)
|
||||
arrow = Arrow(start=ORIGIN, end=RIGHT, buff=0, color=GOLD)
|
||||
vg = VGroup(circle,line,arrow)
|
||||
self.add(vg)
|
||||
anim_kw = {"about_point": arrow.get_start(), "run_time": 1}
|
||||
self.play(Rotating(arrow, 180*DEGREES, **anim_kw))
|
||||
self.play(Rotating(arrow, PI, **anim_kw))
|
||||
self.play(Rotating(vg, PI, about_point=RIGHT))
|
||||
self.play(Rotating(vg, PI, axis=UP, about_point=ORIGIN))
|
||||
self.play(Rotating(vg, PI, axis=RIGHT, about_edge=UP))
|
||||
self.play(vg.animate.move_to(ORIGIN))
|
||||
|
||||
.. manim:: RotatingDifferentAxis
|
||||
|
||||
class RotatingDifferentAxis(ThreeDScene):
|
||||
def construct(self):
|
||||
axes = ThreeDAxes()
|
||||
cube = Cube()
|
||||
arrow2d = Arrow(start=[0, -1.2, 1], end=[0, 1.2, 1], color=YELLOW_E)
|
||||
cube_group = VGroup(cube,arrow2d)
|
||||
self.set_camera_orientation(gamma=0, phi=40*DEGREES, theta=40*DEGREES)
|
||||
self.add(axes, cube_group)
|
||||
play_kw = {"run_time": 1.5}
|
||||
self.play(Rotating(cube_group, PI), **play_kw)
|
||||
self.play(Rotating(cube_group, PI, axis=UP), **play_kw)
|
||||
self.play(Rotating(cube_group, 180*DEGREES, axis=RIGHT), **play_kw)
|
||||
self.wait(0.5)
|
||||
|
||||
See also
|
||||
--------
|
||||
:class:`~.Rotate`, :meth:`~.Mobject.rotate`
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: Mobject,
|
||||
axis: np.ndarray = OUT,
|
||||
radians: np.ndarray = TAU,
|
||||
about_point: np.ndarray | None = None,
|
||||
about_edge: np.ndarray | None = None,
|
||||
angle: float = TAU,
|
||||
axis: Vector3DLike = OUT,
|
||||
about_point: Point3DLike | None = None,
|
||||
about_edge: Vector3DLike | None = None,
|
||||
run_time: float = 5,
|
||||
rate_func: Callable[[float], float] = linear,
|
||||
**kwargs,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
self.angle = angle
|
||||
self.axis = axis
|
||||
self.radians = radians
|
||||
self.about_point = about_point
|
||||
self.about_edge = about_edge
|
||||
super().__init__(mobject, run_time=run_time, rate_func=rate_func, **kwargs)
|
||||
|
|
@ -38,7 +105,7 @@ class Rotating(Animation):
|
|||
def interpolate_mobject(self, alpha: float) -> None:
|
||||
self.mobject.become(self.starting_mobject)
|
||||
self.mobject.rotate(
|
||||
self.rate_func(alpha) * self.radians,
|
||||
self.rate_func(alpha) * self.angle,
|
||||
axis=self.axis,
|
||||
about_point=self.about_point,
|
||||
about_edge=self.about_edge,
|
||||
|
|
@ -59,7 +126,7 @@ class Rotate(Transform):
|
|||
about_point
|
||||
The rotation center.
|
||||
about_edge
|
||||
If ``about_point``is ``None``, this argument specifies
|
||||
If ``about_point`` is ``None``, this argument specifies
|
||||
the direction of the bounding box point to be taken as
|
||||
the rotation center.
|
||||
|
||||
|
|
@ -79,16 +146,20 @@ class Rotate(Transform):
|
|||
Rotate(Square(side_length=0.5), angle=2*PI, rate_func=linear),
|
||||
)
|
||||
|
||||
See also
|
||||
--------
|
||||
:class:`~.Rotating`, :meth:`~.Mobject.rotate`
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: Mobject,
|
||||
angle: float = PI,
|
||||
axis: np.ndarray = OUT,
|
||||
about_point: Sequence[float] | None = None,
|
||||
about_edge: Sequence[float] | None = None,
|
||||
**kwargs,
|
||||
axis: Vector3DLike = OUT,
|
||||
about_point: Point3DLike | None = None,
|
||||
about_edge: Vector3DLike | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
if "path_arc" not in kwargs:
|
||||
kwargs["path_arc"] = angle
|
||||
|
|
@ -102,7 +173,7 @@ class Rotate(Transform):
|
|||
self.about_point = mobject.get_center()
|
||||
super().__init__(mobject, path_arc_centers=self.about_point, **kwargs)
|
||||
|
||||
def create_target(self) -> Mobject:
|
||||
def create_target(self) -> Mobject | OpenGLMobject:
|
||||
target = self.mobject.copy()
|
||||
target.rotate(
|
||||
self.angle,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ from __future__ import annotations
|
|||
|
||||
__all__ = ["Broadcast"]
|
||||
|
||||
from typing import Any, Sequence
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from manim.animation.transform import Restore
|
||||
from manim.mobject.mobject import Mobject
|
||||
|
||||
from ..constants import *
|
||||
from .composition import LaggedStart
|
||||
|
|
@ -49,7 +51,7 @@ class Broadcast(LaggedStart):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject,
|
||||
mobject: Mobject,
|
||||
focal_point: Sequence[float] = ORIGIN,
|
||||
n_mobs: int = 5,
|
||||
initial_opacity: float = 1,
|
||||
|
|
@ -69,10 +71,7 @@ class Broadcast(LaggedStart):
|
|||
anims = []
|
||||
|
||||
# Works by saving the mob that is passed into the animation, scaling it to 0 (or the initial_width) and then restoring the original mob.
|
||||
if mobject.fill_opacity:
|
||||
fill_o = True
|
||||
else:
|
||||
fill_o = False
|
||||
fill_o = bool(mobject.fill_opacity)
|
||||
|
||||
for _ in range(self.n_mobs):
|
||||
mob = mobject.copy()
|
||||
|
|
@ -84,7 +83,7 @@ class Broadcast(LaggedStart):
|
|||
|
||||
mob.move_to(self.focal_point)
|
||||
mob.save_state()
|
||||
mob.set_width(self.initial_width)
|
||||
mob.set(width=self.initial_width)
|
||||
|
||||
if fill_o:
|
||||
mob.set_opacity(self.initial_opacity)
|
||||
|
|
|
|||
|
|
@ -4,15 +4,21 @@ from __future__ import annotations
|
|||
|
||||
import inspect
|
||||
import types
|
||||
from typing import Callable
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from numpy import piecewise
|
||||
|
||||
from ..animation.animation import Animation, Wait, prepare_animation
|
||||
from ..animation.composition import AnimationGroup
|
||||
from ..mobject.mobject import Mobject, Updater, _AnimationBuilder
|
||||
from ..mobject.mobject import Mobject, _AnimationBuilder
|
||||
from ..scene.scene import Scene
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..mobject.mobject import Updater
|
||||
|
||||
__all__ = ["ChangeSpeed"]
|
||||
|
||||
|
||||
class ChangeSpeed(Animation):
|
||||
"""Modifies the speed of passed animation.
|
||||
|
|
@ -108,9 +114,9 @@ class ChangeSpeed(Animation):
|
|||
self.anim = self.setup(anim)
|
||||
|
||||
if affects_speed_updaters:
|
||||
assert (
|
||||
ChangeSpeed.is_changing_dt is False
|
||||
), "Only one animation at a time can play that changes speed (dt) for ChangeSpeed updaters"
|
||||
assert ChangeSpeed.is_changing_dt is False, (
|
||||
"Only one animation at a time can play that changes speed (dt) for ChangeSpeed updaters"
|
||||
)
|
||||
ChangeSpeed.is_changing_dt = True
|
||||
self.t = 0
|
||||
self.affects_speed_updaters = affects_speed_updaters
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@ __all__ = [
|
|||
|
||||
import inspect
|
||||
import types
|
||||
from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence
|
||||
from collections.abc import Callable, Iterable, Sequence
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manim.data_structures import MethodWithArgs
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
|
||||
|
||||
from .. import config
|
||||
|
|
@ -44,11 +46,13 @@ from ..constants import (
|
|||
RendererType,
|
||||
)
|
||||
from ..mobject.mobject import Group, Mobject
|
||||
from ..mobject.types.vectorized_mobject import VGroup
|
||||
from ..utils.paths import path_along_arc, path_along_circles
|
||||
from ..utils.rate_functions import smooth, squish_rate_func
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..scene.scene import Scene
|
||||
from ..typing import Point3DLike, Point3DLike_Array
|
||||
|
||||
|
||||
class Transform(Animation):
|
||||
|
|
@ -122,6 +126,10 @@ class Transform(Animation):
|
|||
|
||||
self.play(*anims, run_time=2)
|
||||
self.wait()
|
||||
|
||||
See also
|
||||
--------
|
||||
:class:`~.ReplacementTransform`, :meth:`~.Mobject.interpolate`, :meth:`~.Mobject.align_data`
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
|
@ -131,12 +139,12 @@ class Transform(Animation):
|
|||
path_func: Callable | None = None,
|
||||
path_arc: float = 0,
|
||||
path_arc_axis: np.ndarray = OUT,
|
||||
path_arc_centers: np.ndarray = None,
|
||||
path_arc_centers: Point3DLike | Point3DLike_Array | None = None,
|
||||
replace_mobject_with_target_in_scene: bool = False,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.path_arc_axis: np.ndarray = path_arc_axis
|
||||
self.path_arc_centers: np.ndarray = path_arc_centers
|
||||
self.path_arc_centers: Point3DLike | Point3DLike_Array | None = path_arc_centers
|
||||
self.path_arc: float = path_arc
|
||||
|
||||
# path_func is a property a few lines below so it doesn't need to be set in any case
|
||||
|
|
@ -203,7 +211,7 @@ class Transform(Animation):
|
|||
self.mobject.align_data(self.target_copy)
|
||||
super().begin()
|
||||
|
||||
def create_target(self) -> Mobject:
|
||||
def create_target(self) -> Mobject | OpenGLMobject:
|
||||
# Has no meaningful effect here, but may be useful
|
||||
# in subclasses
|
||||
return self.target_mobject
|
||||
|
|
@ -228,8 +236,8 @@ class Transform(Animation):
|
|||
self.target_copy,
|
||||
]
|
||||
if config.renderer == RendererType.OPENGL:
|
||||
return zip(*(mob.get_family() for mob in mobs))
|
||||
return zip(*(mob.family_members_with_points() for mob in mobs))
|
||||
return zip(*(mob.get_family() for mob in mobs), strict=True)
|
||||
return zip(*(mob.family_members_with_points() for mob in mobs), strict=True)
|
||||
|
||||
def interpolate_submobject(
|
||||
self,
|
||||
|
|
@ -297,9 +305,7 @@ class ReplacementTransform(Transform):
|
|||
|
||||
|
||||
class TransformFromCopy(Transform):
|
||||
"""
|
||||
Performs a reversed Transform
|
||||
"""
|
||||
"""Preserves a copy of the original VMobject and transforms only it's copy to the target VMobject"""
|
||||
|
||||
def __init__(self, mobject: Mobject, target_mobject: Mobject, **kwargs) -> None:
|
||||
super().__init__(target_mobject, mobject, **kwargs)
|
||||
|
|
@ -430,18 +436,18 @@ class MoveToTarget(Transform):
|
|||
def check_validity_of_input(self, mobject: Mobject) -> None:
|
||||
if not hasattr(mobject, "target"):
|
||||
raise ValueError(
|
||||
"MoveToTarget called on mobject" "without attribute 'target'",
|
||||
"MoveToTarget called on mobjectwithout attribute 'target'",
|
||||
)
|
||||
|
||||
|
||||
class _MethodAnimation(MoveToTarget):
|
||||
def __init__(self, mobject, methods):
|
||||
def __init__(self, mobject: Mobject, methods: list[MethodWithArgs]) -> None:
|
||||
self.methods = methods
|
||||
super().__init__(mobject)
|
||||
|
||||
def finish(self) -> None:
|
||||
for method, method_args, method_kwargs in self.methods:
|
||||
method.__func__(self.mobject, *method_args, **method_kwargs)
|
||||
for item in self.methods:
|
||||
item.method.__func__(self.mobject, *item.args, **item.kwargs)
|
||||
super().finish()
|
||||
|
||||
|
||||
|
|
@ -730,19 +736,36 @@ class CyclicReplace(Transform):
|
|||
def __init__(
|
||||
self, *mobjects: Mobject, path_arc: float = 90 * DEGREES, **kwargs
|
||||
) -> None:
|
||||
self.group = Group(*mobjects)
|
||||
if len(mobjects) == 1 and isinstance(mobjects[0], (Group, VGroup)):
|
||||
self.group = mobjects[0]
|
||||
else:
|
||||
self.group = Group(*mobjects)
|
||||
super().__init__(self.group, path_arc=path_arc, **kwargs)
|
||||
|
||||
def create_target(self) -> Group:
|
||||
def create_target(self) -> Group | VGroup:
|
||||
target = self.group.copy()
|
||||
cycled_targets = [target[-1], *target[:-1]]
|
||||
for m1, m2 in zip(cycled_targets, self.group):
|
||||
for m1, m2 in zip(cycled_targets, self.group, strict=True):
|
||||
m1.move_to(m2)
|
||||
return target
|
||||
|
||||
|
||||
class Swap(CyclicReplace):
|
||||
pass # Renaming, more understandable for two entries
|
||||
"""Another name for :class:`~.CyclicReplace`, which is more understandable for two entries.
|
||||
|
||||
Examples
|
||||
--------
|
||||
.. manim :: SwapExample
|
||||
|
||||
class SwapExample(Scene):
|
||||
def construct(self):
|
||||
text_a = Text("A").move_to(LEFT)
|
||||
text_b = Text("B").move_to(RIGHT)
|
||||
text_group = Group(text_a, text_b)
|
||||
self.play(FadeIn(text_group))
|
||||
self.play(Swap(text_group))
|
||||
self.wait()
|
||||
"""
|
||||
|
||||
|
||||
# TODO, this may be deprecated...worth reimplementing?
|
||||
|
|
@ -830,7 +853,14 @@ class FadeTransform(Transform):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, mobject, target_mobject, stretch=True, dim_to_match=1, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
mobject: Mobject,
|
||||
target_mobject: Mobject,
|
||||
stretch: bool = True,
|
||||
dim_to_match: int = 1,
|
||||
**kwargs: Any,
|
||||
):
|
||||
self.to_add_on_completion = target_mobject
|
||||
self.stretch = stretch
|
||||
self.dim_to_match = dim_to_match
|
||||
|
|
@ -924,5 +954,5 @@ class FadeTransformPieces(FadeTransform):
|
|||
"""Replaces the source submobjects by the target submobjects and sets
|
||||
the opacity to 0.
|
||||
"""
|
||||
for sm0, sm1 in zip(source.get_family(), target.get_family()):
|
||||
for sm0, sm1 in zip(source.get_family(), target.get_family(), strict=True):
|
||||
super().ghost_to(sm0, sm1)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue