mirror of
https://github.com/3b1b/manim.git
synced 2026-01-13 00:18:05 -05:00
Compare commits
1699 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ec00629a5 | ||
|
|
aa6335cd90 | ||
|
|
7093f7d02d | ||
|
|
fad9ed2df7 | ||
|
|
725155409b | ||
|
|
a6675eb043 | ||
|
|
5d2dcec307 | ||
|
|
f60dc7cd07 | ||
|
|
6c39cac62b | ||
|
|
2bd25a55fa | ||
|
|
0e4edfdd79 | ||
|
|
277256a407 | ||
|
|
831b7d455c | ||
|
|
1d14a23af9 | ||
|
|
dffa70ea15 | ||
|
|
31976063df | ||
|
|
aa135280ac | ||
|
|
f0160822ba | ||
|
|
48e07d1817 | ||
|
|
3ef5899a24 | ||
|
|
f895455264 | ||
|
|
3baa14103e | ||
|
|
c315300ff1 | ||
|
|
3b17d6d0eb | ||
|
|
8a29de5ef0 | ||
|
|
ecb729850a | ||
|
|
a770291053 | ||
|
|
27c666fab5 | ||
|
|
942a7e71b8 | ||
|
|
ebb75d1235 | ||
|
|
9af23415a2 | ||
|
|
19778e405a | ||
|
|
833e40c2d4 | ||
|
|
9df53b8a18 | ||
|
|
ff86b0e378 | ||
|
|
92adcd75d4 | ||
|
|
240f5020b4 | ||
|
|
e8205a5049 | ||
|
|
6c8dd14adc | ||
|
|
07f84e2676 | ||
|
|
8db1164ece | ||
|
|
790bf0a104 | ||
|
|
8205edcc4c | ||
|
|
05b3c9852e | ||
|
|
925f2e123f | ||
|
|
565763a2ff | ||
|
|
6a74c241b8 | ||
|
|
416cc8e6d5 | ||
|
|
d694aed452 | ||
|
|
11379283aa | ||
|
|
dd13559b11 | ||
|
|
1658438fef | ||
|
|
f4eb2724c5 | ||
|
|
33f720c73a | ||
|
|
bbb4fa155c | ||
|
|
2318c9e716 | ||
|
|
e80dd243f1 | ||
|
|
3ffe300f96 | ||
|
|
24e3caa072 | ||
|
|
9efd02c500 | ||
|
|
0a318486c5 | ||
|
|
919133c6bf | ||
|
|
066a2ed5dc | ||
|
|
09ced7ce9a | ||
|
|
505b229117 | ||
|
|
5aa8d15d85 | ||
|
|
7aa05572ab | ||
|
|
f1996f8479 | ||
|
|
37b63ca956 | ||
|
|
84fd657d9b | ||
|
|
b489490f41 | ||
|
|
bbf45f95c6 | ||
|
|
b61f1473a5 | ||
|
|
e3d5b49a55 | ||
|
|
4d6a0db1e1 | ||
|
|
0af46e149d | ||
|
|
896b011d76 | ||
|
|
3adaf8e325 | ||
|
|
8762177df5 | ||
|
|
a1d51474ea | ||
|
|
83841ae415 | ||
|
|
b81f244c3c | ||
|
|
7023548ec6 | ||
|
|
758f329a06 | ||
|
|
8f1dfabff0 | ||
|
|
7fa01d5de8 | ||
|
|
0de303d5e0 | ||
|
|
155839bde9 | ||
|
|
3a1e5e1bcf | ||
|
|
264f7b1172 | ||
|
|
85e90a1488 | ||
|
|
f8e6e7df3c | ||
|
|
5dd7cce67f | ||
|
|
f21a4a4696 | ||
|
|
98b0d266d2 | ||
|
|
6821a7c20e | ||
|
|
00f72da493 | ||
|
|
744916507c | ||
|
|
88d863c1d7 | ||
|
|
d7dcc9d76f | ||
|
|
4631508b7d | ||
|
|
8803088121 | ||
|
|
1d466cb299 | ||
|
|
5a1f00b1cb | ||
|
|
17d31045b2 | ||
|
|
950466c1da | ||
|
|
62151e52f1 | ||
|
|
b4ce0b910c | ||
|
|
9dd1f47dab | ||
|
|
49743daf32 | ||
|
|
ba23fbe71e | ||
|
|
ee1594a3cb | ||
|
|
e9afb0ee33 | ||
|
|
8b1715379d | ||
|
|
2501fac32f | ||
|
|
1aec0462ec | ||
|
|
83c70a59d8 | ||
|
|
9b8a6e7ff8 | ||
|
|
758f2ec236 | ||
|
|
d9cac38618 | ||
|
|
e8ebfa312b | ||
|
|
dae24891fa | ||
|
|
a4f9de1ca1 | ||
|
|
697028cd4c | ||
|
|
c84acc0023 | ||
|
|
b1d869cd11 | ||
|
|
13a5f6d6ff | ||
|
|
e3f87d835b | ||
|
|
7ffab788b7 | ||
|
|
bcd09906be | ||
|
|
407c53f97c | ||
|
|
eea3c6b294 | ||
|
|
d2182b9112 | ||
|
|
fbc329d7ce | ||
|
|
25045143a1 | ||
|
|
e899604a2d | ||
|
|
0b898a5594 | ||
|
|
ee2f68cd49 | ||
|
|
2cce4ccdd7 | ||
|
|
f3ecebee43 | ||
|
|
e764da3c3a | ||
|
|
fbbea47d11 | ||
|
|
781a9934fd | ||
|
|
a7173142bf | ||
|
|
0e78027186 | ||
|
|
82bd02d21f | ||
|
|
d065e1973d | ||
|
|
7070777408 | ||
|
|
5c2a9f2129 | ||
|
|
1b695e1c19 | ||
|
|
da1cc44d90 | ||
|
|
3bbb759112 | ||
|
|
41c6cbcb59 | ||
|
|
5930e6a176 | ||
|
|
8f3ff91165 | ||
|
|
b12677bc1a | ||
|
|
cdec64e3f1 | ||
|
|
2dc8bc9b9c | ||
|
|
94f0bf557a | ||
|
|
e20690b7c1 | ||
|
|
2c7689ed9e | ||
|
|
c73d507c76 | ||
|
|
317a5d6226 | ||
|
|
4339f97c56 | ||
|
|
81c3ae3037 | ||
|
|
61b04079f5 | ||
|
|
5a0e5a16ea | ||
|
|
f0b5181694 | ||
|
|
185782a2e7 | ||
|
|
8ab95ebe9d | ||
|
|
77159eea2e | ||
|
|
6766e459f2 | ||
|
|
01f4ef3e5d | ||
|
|
b531c82bc4 | ||
|
|
5d942d5ac0 | ||
|
|
b285ca7c22 | ||
|
|
82540edae9 | ||
|
|
f9a6fa7036 | ||
|
|
4eabaecfc8 | ||
|
|
b881e55fca | ||
|
|
f1c50640a3 | ||
|
|
deb1311e48 | ||
|
|
82fa6ab125 | ||
|
|
4d91ff3f2f | ||
|
|
b6f9da87d0 | ||
|
|
c60e97ebf9 | ||
|
|
b1ed16e81a | ||
|
|
c94ebaa260 | ||
|
|
030fb52018 | ||
|
|
487f582302 | ||
|
|
6d0c55d2ba | ||
|
|
c82f60e29e | ||
|
|
c03279d626 | ||
|
|
7b72fa8ca1 | ||
|
|
8b454fbe93 | ||
|
|
f77e25ff86 | ||
|
|
872ef67cf7 | ||
|
|
305ca72ebe | ||
|
|
4d81d3678b | ||
|
|
55e968e174 | ||
|
|
97d1609849 | ||
|
|
e10f850d0d | ||
|
|
b8584fe5ab | ||
|
|
b6c23a09e9 | ||
|
|
0e574882b3 | ||
|
|
bee3470856 | ||
|
|
ed3d44120c | ||
|
|
4466cfe727 | ||
|
|
e9aba0b92c | ||
|
|
6cdbe0d67a | ||
|
|
7732d2f0ee | ||
|
|
f77482c864 | ||
|
|
23ebbb2af1 | ||
|
|
14fbed76da | ||
|
|
e10a752c00 | ||
|
|
fde82e09c0 | ||
|
|
cfd362aa56 | ||
|
|
329d2c6eae | ||
|
|
f22a341e84 | ||
|
|
2d115a2c90 | ||
|
|
c726eb7a18 | ||
|
|
33fa76dfac | ||
|
|
0021880fba | ||
|
|
ed99427a3b | ||
|
|
7425057d9f | ||
|
|
ef5253f1bc | ||
|
|
fbccb1ebf3 | ||
|
|
f626a1a1e2 | ||
|
|
c1242d2dd5 | ||
|
|
719c81d72b | ||
|
|
ed1fc4d5f9 | ||
|
|
3822b00bec | ||
|
|
2753beb7bb | ||
|
|
a4afbfd739 | ||
|
|
5f878a2c1a | ||
|
|
9483f26a3b | ||
|
|
b4132e3d5e | ||
|
|
e9b404406d | ||
|
|
b543cc0e32 | ||
|
|
d45ea28dc1 | ||
|
|
788775e419 | ||
|
|
1bca0e63e9 | ||
|
|
54ad3550ef | ||
|
|
d19b386415 | ||
|
|
e359f520bc | ||
|
|
696fc85ff7 | ||
|
|
add1daf500 | ||
|
|
242e4a3471 | ||
|
|
9e563ae3b4 | ||
|
|
da909c0df8 | ||
|
|
0239e12d8a | ||
|
|
0fd8fdc3ca | ||
|
|
762f1abef7 | ||
|
|
17c2772b84 | ||
|
|
0d2d1b5c03 | ||
|
|
4ce123be44 | ||
|
|
d5a88d0fa4 | ||
|
|
3b146636b4 | ||
|
|
b24ba19dec | ||
|
|
0dc096bf57 | ||
|
|
e40a2935b1 | ||
|
|
f84b8a66fe | ||
|
|
952a598e3b | ||
|
|
c635f19f2a | ||
|
|
8645894255 | ||
|
|
e712951f2d | ||
|
|
1b24074369 | ||
|
|
acba13f499 | ||
|
|
61aec6051a | ||
|
|
9a78d13212 | ||
|
|
0787c4f362 | ||
|
|
0b7b3f4f31 | ||
|
|
7356a36fa7 | ||
|
|
f3e3a7c56f | ||
|
|
a35dd5a3cb | ||
|
|
7b4199c674 | ||
|
|
d8378d8157 | ||
|
|
8647a6429d | ||
|
|
3bb8f3f042 | ||
|
|
56df15453f | ||
|
|
d50717a3fc | ||
|
|
ca9b70e218 | ||
|
|
25c5aa2c65 | ||
|
|
c08ea4e645 | ||
|
|
573d630e5b | ||
|
|
6d72893382 | ||
|
|
40290ada83 | ||
|
|
bd356daa99 | ||
|
|
b667db2d31 | ||
|
|
f92211b352 | ||
|
|
1c2b52a128 | ||
|
|
eb315daeda | ||
|
|
e151334675 | ||
|
|
bbeba108bc | ||
|
|
77ce17679c | ||
|
|
7fa2654d8a | ||
|
|
ec620fa849 | ||
|
|
da53a6f808 | ||
|
|
1e621e8278 | ||
|
|
31119b630e | ||
|
|
b0fd520382 | ||
|
|
c1e14ef5b6 | ||
|
|
e9470b6bde | ||
|
|
5c0a1e4b76 | ||
|
|
121e6215f8 | ||
|
|
3d5642f3d7 | ||
|
|
9df58e4ddf | ||
|
|
f09092024f | ||
|
|
4d65c97965 | ||
|
|
76966064ce | ||
|
|
152d03ed27 | ||
|
|
8624168ed9 | ||
|
|
354db4423f | ||
|
|
27344249de | ||
|
|
0b3a1b271c | ||
|
|
fd8904ec83 | ||
|
|
7da6179493 | ||
|
|
17452dcd10 | ||
|
|
2f5acc6a87 | ||
|
|
71f018dfff | ||
|
|
b3ae517a05 | ||
|
|
f7bb5c1b8c | ||
|
|
a3227dda67 | ||
|
|
2ed78c6e0f | ||
|
|
8aa004b0b1 | ||
|
|
45938dd76f | ||
|
|
3fe4d6d2d4 | ||
|
|
a18600e8a4 | ||
|
|
700418a79c | ||
|
|
4940ccac7d | ||
|
|
17d7f0b6f0 | ||
|
|
275cf94b06 | ||
|
|
54fff5523b | ||
|
|
5707585d17 | ||
|
|
0305582e64 | ||
|
|
a4c3bb03d1 | ||
|
|
b00d718431 | ||
|
|
6da5d4c8f6 | ||
|
|
01670cf823 | ||
|
|
5986d0e7d2 | ||
|
|
d384fc1e27 | ||
|
|
8aedb8f33e | ||
|
|
bccc17a3ac | ||
|
|
892df54c9f | ||
|
|
663c57ba74 | ||
|
|
26e9b9cd7c | ||
|
|
a99ccea02c | ||
|
|
892ce2db09 | ||
|
|
d14f22c5ba | ||
|
|
846c10a0ff | ||
|
|
128178b46e | ||
|
|
b4f23e8d8e | ||
|
|
5765ab9055 | ||
|
|
6eb7edc664 | ||
|
|
e836c3bb42 | ||
|
|
5ff8e28ba5 | ||
|
|
6dc1ecb00a | ||
|
|
be78f5257a | ||
|
|
798479536d | ||
|
|
226df63d0b | ||
|
|
b8fb69773e | ||
|
|
fec2306f9a | ||
|
|
17d75bd336 | ||
|
|
23662d093f | ||
|
|
34d4689672 | ||
|
|
607ef334e9 | ||
|
|
b4b4d39ec5 | ||
|
|
1c2942798e | ||
|
|
e9ea5fbea0 | ||
|
|
7ecfc041b3 | ||
|
|
44a9c6337e | ||
|
|
d1a5089acc | ||
|
|
9e5f39a4a9 | ||
|
|
6da93cde7b | ||
|
|
6340db1076 | ||
|
|
f45d81be11 | ||
|
|
baa2adc128 | ||
|
|
f16277f100 | ||
|
|
876f06cc37 | ||
|
|
f682bf97e3 | ||
|
|
025639f026 | ||
|
|
e885ec6ecd | ||
|
|
3b3150c3c5 | ||
|
|
33aa4e979a | ||
|
|
9de7a6477d | ||
|
|
19b8057136 | ||
|
|
b6dd6fe16d | ||
|
|
51b2984ee3 | ||
|
|
957eedc32c | ||
|
|
2614b34d11 | ||
|
|
4ea1d6d64f | ||
|
|
fd67858bb0 | ||
|
|
442e7fc14d | ||
|
|
bb27a78449 | ||
|
|
a06d5613f4 | ||
|
|
6605ab75e8 | ||
|
|
ed9a4bd9eb | ||
|
|
d54b796154 | ||
|
|
bb72718c3b | ||
|
|
d279272762 | ||
|
|
f2f652f85d | ||
|
|
bf9d797d84 | ||
|
|
29e5a8bc90 | ||
|
|
02bad81fc3 | ||
|
|
2bfe7e08ff | ||
|
|
e727faaccb | ||
|
|
6b911f5721 | ||
|
|
565ff09d58 | ||
|
|
4b4a973464 | ||
|
|
8e2799a499 | ||
|
|
a44e230a07 | ||
|
|
f9fb68c011 | ||
|
|
c1ad893030 | ||
|
|
65d0826b91 | ||
|
|
41120b096e | ||
|
|
1f6e911d60 | ||
|
|
c45ff910f0 | ||
|
|
15760cf253 | ||
|
|
f6291d7e82 | ||
|
|
b5e6177afd | ||
|
|
22d9c57f60 | ||
|
|
3a992e136d | ||
|
|
12ef0a26d7 | ||
|
|
7a11e3d20f | ||
|
|
cf63dfddf9 | ||
|
|
42d8888f8e | ||
|
|
322f138490 | ||
|
|
df657c06c2 | ||
|
|
0c61c908b2 | ||
|
|
82658e1db3 | ||
|
|
de9ecbd766 | ||
|
|
ca9f4357fa | ||
|
|
e95aa69c4c | ||
|
|
6997cc9501 | ||
|
|
7f47815230 | ||
|
|
d3e61b962b | ||
|
|
8999ebb556 | ||
|
|
88f0c24c69 | ||
|
|
09579fcd3e | ||
|
|
01d989ba23 | ||
|
|
6c3e4b94ea | ||
|
|
52baf5b7c2 | ||
|
|
fd18e4a21f | ||
|
|
2a1b023442 | ||
|
|
288983e7b9 | ||
|
|
f6ff070a8e | ||
|
|
5126dd1f52 | ||
|
|
8345ca6160 | ||
|
|
611ac7f448 | ||
|
|
933b7fd3da | ||
|
|
15f3b359ae | ||
|
|
0e326c7ac5 | ||
|
|
ed2e3e80d9 | ||
|
|
3c240478b8 | ||
|
|
120d26defa | ||
|
|
5c427ea287 | ||
|
|
503bd116a6 | ||
|
|
f5d5565af1 | ||
|
|
aedf5633aa | ||
|
|
63b497c352 | ||
|
|
531a031b50 | ||
|
|
b48ce3f1de | ||
|
|
5636b41dfd | ||
|
|
402c06c99a | ||
|
|
eec396681c | ||
|
|
d06b3769b8 | ||
|
|
8fcb069808 | ||
|
|
9fb6280f1d | ||
|
|
e35f8466be | ||
|
|
44df81fd70 | ||
|
|
d1fc6c8ed7 | ||
|
|
9d1c8df095 | ||
|
|
1d0b864001 | ||
|
|
5008e20b8e | ||
|
|
c92b6dbd0b | ||
|
|
9c23a5feef | ||
|
|
ba3bb64bce | ||
|
|
448d792473 | ||
|
|
2f202e26b2 | ||
|
|
f9b1167d14 | ||
|
|
4179c1c3e9 | ||
|
|
0c25b56afe | ||
|
|
0f95e80798 | ||
|
|
b7e2abb801 | ||
|
|
1543450a4f | ||
|
|
c9e223485c | ||
|
|
9bf51bda02 | ||
|
|
a46b7d6931 | ||
|
|
8f502f2790 | ||
|
|
c30b535504 | ||
|
|
a5d5e40f6a | ||
|
|
ad332c5863 | ||
|
|
96d9a66faf | ||
|
|
368a6c3d1c | ||
|
|
0c3b401d34 | ||
|
|
9df136c723 | ||
|
|
e1e7b1e6c1 | ||
|
|
e7240547fb | ||
|
|
9158b7a0c2 | ||
|
|
dba0550da5 | ||
|
|
5df4e68f1b | ||
|
|
787b3be357 | ||
|
|
c700a27cad | ||
|
|
d7f3f5ad24 | ||
|
|
1798875396 | ||
|
|
0cd0a56243 | ||
|
|
6e2dcae4d7 | ||
|
|
17b2efeb71 | ||
|
|
4ff876b536 | ||
|
|
cbe24daa98 | ||
|
|
fdb6673238 | ||
|
|
156b83c38e | ||
|
|
2d83cccc0a | ||
|
|
cfd32c79b1 | ||
|
|
d24ba30fde | ||
|
|
558f2652a4 | ||
|
|
de3916424f | ||
|
|
3bf956871b | ||
|
|
84f032610f | ||
|
|
36e8421395 | ||
|
|
3770fae6cf | ||
|
|
ddda43e03e | ||
|
|
cb4b67655b | ||
|
|
7c683c8992 | ||
|
|
6331f76c12 | ||
|
|
944128d9bf | ||
|
|
b374a07d76 | ||
|
|
24d9d9e45d | ||
|
|
f0cbc0571f | ||
|
|
608b2d9bce | ||
|
|
47d4b0236e | ||
|
|
41a02285bd | ||
|
|
00e9c426ff | ||
|
|
69235f0a21 | ||
|
|
e8f288f753 | ||
|
|
1084dfb681 | ||
|
|
12e8506841 | ||
|
|
f984443ed5 | ||
|
|
479aac1988 | ||
|
|
97e7b0568e | ||
|
|
9a79043640 | ||
|
|
4d644538b1 | ||
|
|
95191325e8 | ||
|
|
700d7c6def | ||
|
|
36107c28ef | ||
|
|
00fe33957c | ||
|
|
cbe016391b | ||
|
|
3fa68ad60c | ||
|
|
e3d09d161c | ||
|
|
2394bdc2e6 | ||
|
|
634c3d672e | ||
|
|
b558ae98cf | ||
|
|
e6fc323b6d | ||
|
|
c3ccfbfa65 | ||
|
|
6f69902892 | ||
|
|
1a5a59f40e | ||
|
|
2493458e45 | ||
|
|
1ab60f1c3e | ||
|
|
e677f4027a | ||
|
|
03bea8198c | ||
|
|
28650b27dc | ||
|
|
2c55f93512 | ||
|
|
567e62de03 | ||
|
|
a2f201f59e | ||
|
|
514301e0af | ||
|
|
71f328c7ef | ||
|
|
66817c4e2b | ||
|
|
f87b82a9e9 | ||
|
|
2a3db89a2d | ||
|
|
a63dd119c1 | ||
|
|
f786eb68d1 | ||
|
|
25cd846356 | ||
|
|
7a44d66ebc | ||
|
|
4de31aba7f | ||
|
|
43614394a4 | ||
|
|
ed0ac3bee4 | ||
|
|
94894c0fe3 | ||
|
|
f05f94fbf5 | ||
|
|
e7251bcc2f | ||
|
|
c0f6e94545 | ||
|
|
390bfac9df | ||
|
|
269128dfb6 | ||
|
|
8f52c7bda8 | ||
|
|
5eafed2c31 | ||
|
|
4b6a3a2599 | ||
|
|
0e5423807c | ||
|
|
60605e8442 | ||
|
|
b1efee5088 | ||
|
|
820e1840d3 | ||
|
|
10c1f054ca | ||
|
|
1727d4b86a | ||
|
|
2d4ffc989e | ||
|
|
a45e0afe0e | ||
|
|
f13c41bcbb | ||
|
|
65f4f4bd29 | ||
|
|
521e3ef143 | ||
|
|
7dffa24acd | ||
|
|
0726dccd0a | ||
|
|
003fafc20f | ||
|
|
d981ee47e2 | ||
|
|
46294a5fad | ||
|
|
d7e63d907a | ||
|
|
afd1ef65f2 | ||
|
|
5b9672bb59 | ||
|
|
c2b2cb3b7e | ||
|
|
c040dba835 | ||
|
|
0a5414553f | ||
|
|
6fde779944 | ||
|
|
c8f4c49720 | ||
|
|
5e3ed6f860 | ||
|
|
5a16e3a358 | ||
|
|
fc6953e102 | ||
|
|
3938f81c1b | ||
|
|
0c6149c9dd | ||
|
|
ddcd052036 | ||
|
|
2fbe0c6ee6 | ||
|
|
1097f0df96 | ||
|
|
04a259950f | ||
|
|
37f1130de6 | ||
|
|
dda9683fb7 | ||
|
|
729dae1376 | ||
|
|
32632367b6 | ||
|
|
46309e26c7 | ||
|
|
dcffbe21d5 | ||
|
|
c5b0040166 | ||
|
|
5b6997aea9 | ||
|
|
7a6a8556b8 | ||
|
|
e4df135bbe | ||
|
|
9916f56fb1 | ||
|
|
74365065fe | ||
|
|
7b614bc968 | ||
|
|
a4dbd0881b | ||
|
|
e9ec5e0c93 | ||
|
|
143634742b | ||
|
|
c32104b7ca | ||
|
|
2d19ee8264 | ||
|
|
f4c82b62af | ||
|
|
55e8d3bc55 | ||
|
|
be5762ba9c | ||
|
|
3ec8bb0d28 | ||
|
|
98a25eaefe | ||
|
|
fc01239c7a | ||
|
|
331e5a1c21 | ||
|
|
0e80799b1f | ||
|
|
fbb0d76f4f | ||
|
|
2061f95ef5 | ||
|
|
82eb0ae5be | ||
|
|
5cc30df2ac | ||
|
|
fb8280171b | ||
|
|
fc482c107f | ||
|
|
384304e92b | ||
|
|
fdcac10861 | ||
|
|
fe85d4e02f | ||
|
|
8f9ce57de5 | ||
|
|
1253c18c54 | ||
|
|
3dc0c7b0cb | ||
|
|
09ae714edb | ||
|
|
d6b23324a6 | ||
|
|
b63ba1c7a4 | ||
|
|
b00c93dc88 | ||
|
|
5fb06e77b5 | ||
|
|
d5d13c3174 | ||
|
|
9b3332ad02 | ||
|
|
3c0f8abe5f | ||
|
|
53cfbacab4 | ||
|
|
e3b74ffd7a | ||
|
|
52dd06ce6f | ||
|
|
90293f3907 | ||
|
|
32e5b2b4b1 | ||
|
|
c292046913 | ||
|
|
73edeaa153 | ||
|
|
375bc2073d | ||
|
|
b353c4f21c | ||
|
|
367e243066 | ||
|
|
5a2a363a87 | ||
|
|
baab9ddf02 | ||
|
|
c1ebb583c8 | ||
|
|
696e952a3a | ||
|
|
a3754fa606 | ||
|
|
5de62edfb8 | ||
|
|
c39d208ff0 | ||
|
|
a47b7c6bae | ||
|
|
4aa6be6e55 | ||
|
|
f900669bc4 | ||
|
|
06ba7031c7 | ||
|
|
2fd4d7a2c4 | ||
|
|
cd79151c10 | ||
|
|
a7697095d6 | ||
|
|
42c59f80c8 | ||
|
|
c68bf1c8f7 | ||
|
|
ddcfd01fa3 | ||
|
|
bb62ff4cd6 | ||
|
|
c0852eae88 | ||
|
|
4494d80a4c | ||
|
|
14cbfc0423 | ||
|
|
539f3b095a | ||
|
|
5dbc7d807f | ||
|
|
d04d451c6a | ||
|
|
a58772db87 | ||
|
|
0ac155d150 | ||
|
|
090743aacb | ||
|
|
9bd4f6714f | ||
|
|
e4f888fbc4 | ||
|
|
e08784400a | ||
|
|
20b787223b | ||
|
|
9a502cd83b | ||
|
|
78ac18496d | ||
|
|
59af903f14 | ||
|
|
e218105f73 | ||
|
|
f91c81dd6a | ||
|
|
3bd56bff07 | ||
|
|
68a8114d7a | ||
|
|
9c524fe3a7 | ||
|
|
b3c8940a18 | ||
|
|
1b1b138860 | ||
|
|
19e93454d9 | ||
|
|
86bfa4c39c | ||
|
|
cfa8577454 | ||
|
|
b7bd40a6f6 | ||
|
|
453ccc2765 | ||
|
|
f6b5edede2 | ||
|
|
3d118a5bb6 | ||
|
|
314c847463 | ||
|
|
7f27e926f2 | ||
|
|
5f6b653803 | ||
|
|
83a9217063 | ||
|
|
2daf62ecea | ||
|
|
c698c6ea44 | ||
|
|
4e4a7b9886 | ||
|
|
b423a423b5 | ||
|
|
d37de184d2 | ||
|
|
bec3ba70eb | ||
|
|
08e22cf859 | ||
|
|
1f3b058aa9 | ||
|
|
d4c89e520f | ||
|
|
7c04edcced | ||
|
|
97ecfcda9b | ||
|
|
c6fcaa93bb | ||
|
|
fa3cad18e0 | ||
|
|
f696ba4100 | ||
|
|
1d1706039e | ||
|
|
78848b971c | ||
|
|
79b8136330 | ||
|
|
f314054081 | ||
|
|
7b67f4556b | ||
|
|
8f6b006cc8 | ||
|
|
281b38b860 | ||
|
|
f90e4147b6 | ||
|
|
d53dbba346 | ||
|
|
098b939ec0 | ||
|
|
8b83c5e27d | ||
|
|
2c27511556 | ||
|
|
9f5f682351 | ||
|
|
39cdae0983 | ||
|
|
b40a3b5710 | ||
|
|
38b305bb19 | ||
|
|
8cc484943e | ||
|
|
d29cfd842f | ||
|
|
2e3a112ff8 | ||
|
|
d65a915e7b | ||
|
|
5f69899e65 | ||
|
|
5e09a80c5b | ||
|
|
f61dfe7948 | ||
|
|
bf3571c103 | ||
|
|
20593d8fa8 | ||
|
|
6537258f24 | ||
|
|
fda7fff146 | ||
|
|
00333d0a4d | ||
|
|
ad60b903d0 | ||
|
|
091cd355d4 | ||
|
|
dd2a459eaa | ||
|
|
c5aa330077 | ||
|
|
bbe79e0026 | ||
|
|
7af100489b | ||
|
|
f1c32b64cc | ||
|
|
cc094dd479 | ||
|
|
ab0d5c58d4 | ||
|
|
4abf93e2aa | ||
|
|
7c8162a6f7 | ||
|
|
c4f190cfc4 | ||
|
|
d60ec48591 | ||
|
|
ba8fb4a6b7 | ||
|
|
ca1c2f7797 | ||
|
|
0d238417cb | ||
|
|
ddfc3a6567 | ||
|
|
6b29691e0e | ||
|
|
6b451dcc22 | ||
|
|
4d9498322e | ||
|
|
d1b888958b | ||
|
|
d6a00bc32d | ||
|
|
c408adeefa | ||
|
|
a5dd08cca7 | ||
|
|
30dc8176ff | ||
|
|
96fbac85ad | ||
|
|
9314dfd933 | ||
|
|
b3335c65fb | ||
|
|
fb0de62ef4 | ||
|
|
bf73938fd1 | ||
|
|
8d14b66583 | ||
|
|
92b0953081 | ||
|
|
e98ebfe9e1 | ||
|
|
889acea380 | ||
|
|
0b41457d4e | ||
|
|
fdcc8d4257 | ||
|
|
a7af5e72c6 | ||
|
|
677d609870 | ||
|
|
a429a146c7 | ||
|
|
ac4755459a | ||
|
|
ff37b589ff | ||
|
|
81e359d22e | ||
|
|
632c35daef | ||
|
|
f47f848236 | ||
|
|
cb87912203 | ||
|
|
9a1e103a7f | ||
|
|
8c7e93f829 | ||
|
|
488387f1de | ||
|
|
7db1f7786a | ||
|
|
067f25d4b8 | ||
|
|
42a3c56898 | ||
|
|
7b8677fd6c | ||
|
|
5eee39803f | ||
|
|
6259d1c897 | ||
|
|
60c03831fb | ||
|
|
9122aa2419 | ||
|
|
007680a183 | ||
|
|
c2b5595fae | ||
|
|
5939f24cd4 | ||
|
|
cfe61a35af | ||
|
|
c1298bdffd | ||
|
|
e187728f0c | ||
|
|
4a19c48667 | ||
|
|
63692ed59a | ||
|
|
4ba5e733fb | ||
|
|
20f5ba409f | ||
|
|
c65a7fb340 | ||
|
|
b13b94938a | ||
|
|
825d6bdc5c | ||
|
|
2451f024f6 | ||
|
|
62bb40a7df | ||
|
|
065df65cf1 | ||
|
|
6486938c59 | ||
|
|
9d7b2873bb | ||
|
|
3c1ab6ce25 | ||
|
|
446a69f7c0 | ||
|
|
5b454a3ff7 | ||
|
|
9ee1a526ed | ||
|
|
06f0374143 | ||
|
|
dde085094f | ||
|
|
f07bd23235 | ||
|
|
c0b90b398c | ||
|
|
92386f4e20 | ||
|
|
38f9686d68 | ||
|
|
349a9c9cea | ||
|
|
556ffb192f | ||
|
|
bfa36a7659 | ||
|
|
2d709758d0 | ||
|
|
509f3db371 | ||
|
|
bc95013ab4 | ||
|
|
8644d71820 | ||
|
|
2cdebeda29 | ||
|
|
a6d484236c | ||
|
|
131794e53c | ||
|
|
a4ea4791e7 | ||
|
|
6c0f00b4a1 | ||
|
|
c87b2e272b | ||
|
|
31e6aa2ae0 | ||
|
|
369a7fc455 | ||
|
|
e2fc0ab1aa | ||
|
|
494f1899fd | ||
|
|
b22f1ab715 | ||
|
|
156ea11537 | ||
|
|
baff788217 | ||
|
|
e43b8ef4e4 | ||
|
|
b8b44f062c | ||
|
|
504f5c4329 | ||
|
|
a4b8a8d88c | ||
|
|
afebc29440 | ||
|
|
148afc29aa | ||
|
|
572383f541 | ||
|
|
c2b16d768e | ||
|
|
e5c79cbb90 | ||
|
|
16ea2fd9c1 | ||
|
|
5541d55094 | ||
|
|
05887d5039 | ||
|
|
d1c6b80622 | ||
|
|
3b26a6c260 | ||
|
|
32b76bf448 | ||
|
|
6da1836f02 | ||
|
|
e01496e8bd | ||
|
|
93fe783b80 | ||
|
|
7b3f4d08f5 | ||
|
|
d2518df82c | ||
|
|
f11ab03112 | ||
|
|
d4207540bb | ||
|
|
592432611b | ||
|
|
cf01a215db | ||
|
|
ba46164b49 | ||
|
|
525309f277 | ||
|
|
343253b787 | ||
|
|
123450c3f2 | ||
|
|
5424716877 | ||
|
|
2a61360786 | ||
|
|
471eb8b0d9 | ||
|
|
d221073360 | ||
|
|
8b2785fdc9 | ||
|
|
d28f30dc4a | ||
|
|
23af9c6c9c | ||
|
|
38b293c204 | ||
|
|
b54e9efccf | ||
|
|
e3acb920f4 | ||
|
|
915b7ea3ad | ||
|
|
23f7bce427 | ||
|
|
5a238f1551 | ||
|
|
3821dd358a | ||
|
|
68e47c839b | ||
|
|
e59d839806 | ||
|
|
e737ace268 | ||
|
|
3a1f07492c | ||
|
|
58dbec76da | ||
|
|
c3643cf4e3 | ||
|
|
917ff685bd | ||
|
|
1d5cee6f43 | ||
|
|
27cd86fde7 | ||
|
|
cc73075d63 | ||
|
|
607e918ab1 | ||
|
|
1237ecc8f0 | ||
|
|
df6ea06f89 | ||
|
|
2461248ef6 | ||
|
|
9dc4551685 | ||
|
|
d650cfd3e7 | ||
|
|
c71eddb9d3 | ||
|
|
6f81ec383c | ||
|
|
af0948ea15 | ||
|
|
d702396d87 | ||
|
|
b2f09dbdb1 | ||
|
|
bbfce0f5be | ||
|
|
c65dfb85d2 | ||
|
|
b64875fd55 | ||
|
|
6ead1ceca6 | ||
|
|
f08003fcb2 | ||
|
|
102af22182 | ||
|
|
f934bdc61e | ||
|
|
ba405521ce | ||
|
|
171042b8d7 | ||
|
|
25bab8c10e | ||
|
|
da972edfca | ||
|
|
32c5534099 | ||
|
|
1f36e83c9d | ||
|
|
4e54c6de54 | ||
|
|
3bc4416655 | ||
|
|
0dc8e4389d | ||
|
|
34ec0f4795 | ||
|
|
faa52d7dd4 | ||
|
|
d797e5f1fe | ||
|
|
99952067c1 | ||
|
|
30ae89464e | ||
|
|
1ecbbbbcd5 | ||
|
|
186b86cb70 | ||
|
|
4c42083ffc | ||
|
|
dec753fc26 | ||
|
|
08c90c272b | ||
|
|
be8b28dd9d | ||
|
|
d0dc7ed301 | ||
|
|
abc24f2d52 | ||
|
|
53756ea140 | ||
|
|
dcb608f258 | ||
|
|
05acf31e47 | ||
|
|
bc5c3968d9 | ||
|
|
11c451c2e9 | ||
|
|
52c5e32ba5 | ||
|
|
37efc93e9c | ||
|
|
d63f61b903 | ||
|
|
49d53d9a8f | ||
|
|
79bf87754a | ||
|
|
e336cf5ddc | ||
|
|
e9638aee2e | ||
|
|
169e99b69e | ||
|
|
34b4d90b11 | ||
|
|
f89676d53f | ||
|
|
821857afa8 | ||
|
|
35a2ade954 | ||
|
|
a42d9c6ecb | ||
|
|
9f945d9940 | ||
|
|
852e840523 | ||
|
|
054b828396 | ||
|
|
2dac041006 | ||
|
|
2708ecfd47 | ||
|
|
9084ddbd06 | ||
|
|
fc153bb49a | ||
|
|
537a2c74aa | ||
|
|
dd6e99ab2e | ||
|
|
26834178e7 | ||
|
|
fd17e42c23 | ||
|
|
c1143c875d | ||
|
|
a822c93019 | ||
|
|
caca6d0c0e | ||
|
|
c1a1b0f145 | ||
|
|
33d4dd7729 | ||
|
|
0c37d91af3 | ||
|
|
ae590e7fde | ||
|
|
150b03427d | ||
|
|
7556b99dc0 | ||
|
|
960e4e13eb | ||
|
|
e7a60c6c85 | ||
|
|
72472c5836 | ||
|
|
ae8e8040d7 | ||
|
|
5ee4b94ec3 | ||
|
|
93f7671732 | ||
|
|
cf0afb0259 | ||
|
|
dbe33f9382 | ||
|
|
f31cf2e30f | ||
|
|
81715adb05 | ||
|
|
9174ec335c | ||
|
|
25f672cc55 | ||
|
|
9a44794dc9 | ||
|
|
46b52a1bbc | ||
|
|
5e7913fdd4 | ||
|
|
61424dbfec | ||
|
|
2671817ae9 | ||
|
|
165bf2fe6e | ||
|
|
feb6a33135 | ||
|
|
cd371dc207 | ||
|
|
2b3bd2bfce | ||
|
|
978137b143 | ||
|
|
f11cb5a498 | ||
|
|
0b58572f41 | ||
|
|
439e83f40e | ||
|
|
279f66ceaf | ||
|
|
8af879236e | ||
|
|
1cab58ac49 | ||
|
|
41fd6d4f4b | ||
|
|
13bcd723cf | ||
|
|
14cac5ce71 | ||
|
|
6a735e49b8 | ||
|
|
b443c9e764 | ||
|
|
90db32ee55 | ||
|
|
54bde86c7b | ||
|
|
10c6bfe3ad | ||
|
|
26ce1d86ab | ||
|
|
6ba1eae8f6 | ||
|
|
60e1c88eb4 | ||
|
|
661b81ab17 | ||
|
|
5d371bfec9 | ||
|
|
4910501ee0 | ||
|
|
9a2b2ecd9e | ||
|
|
9d03f40be2 | ||
|
|
9795beba89 | ||
|
|
212cdbb4d2 | ||
|
|
72bfb0047e | ||
|
|
9b9dca7bed | ||
|
|
a05621e857 | ||
|
|
c2feb6fff1 | ||
|
|
21bf3c3e3a | ||
|
|
6451371a75 | ||
|
|
5b1ce542d7 | ||
|
|
1f6308577b | ||
|
|
c8195f72a1 | ||
|
|
c653610215 | ||
|
|
09df42a755 | ||
|
|
687d70e9b6 | ||
|
|
ea929d167f | ||
|
|
fd1f4313ec | ||
|
|
81f6063736 | ||
|
|
4fdd655564 | ||
|
|
bb66a7c977 | ||
|
|
d4635ab165 | ||
|
|
48801f2892 | ||
|
|
0a89a775a2 | ||
|
|
a0006426cc | ||
|
|
eee4054da8 | ||
|
|
b16b107f3e | ||
|
|
138c48c739 | ||
|
|
5ce8d08e0f | ||
|
|
d1f1ed6a1a | ||
|
|
8c9ea90d50 | ||
|
|
e6f73073fe | ||
|
|
c45fe52a70 | ||
|
|
6a458547c3 | ||
|
|
739667f8a9 | ||
|
|
edec91e1e4 | ||
|
|
1e6eef89ec | ||
|
|
9d772496dd | ||
|
|
7a152fed1c | ||
|
|
00ad9d7f95 | ||
|
|
222b6d1220 | ||
|
|
145c6a7e78 | ||
|
|
f5b4a86801 | ||
|
|
46bcf938ed | ||
|
|
81cedfbfda | ||
|
|
61a3bd8102 | ||
|
|
9d5282b74f | ||
|
|
512c42f4df | ||
|
|
62e5efe87b | ||
|
|
429d63eebf | ||
|
|
2317a7d982 | ||
|
|
8a060dfa3f | ||
|
|
a4d4ae9b47 | ||
|
|
cf8790eefa | ||
|
|
2208b51f1b | ||
|
|
5584521fb0 | ||
|
|
7c4874bdad | ||
|
|
3e913b6649 | ||
|
|
1f129f4a23 | ||
|
|
c7a50ac7a5 | ||
|
|
940432e320 | ||
|
|
ee7218745e | ||
|
|
b91284b4aa | ||
|
|
f0a36329b1 | ||
|
|
ccefbc0dea | ||
|
|
5d34cee014 | ||
|
|
be904774dd | ||
|
|
bab809b9a6 | ||
|
|
39230a805c | ||
|
|
596b7c0112 | ||
|
|
97304d9d86 | ||
|
|
2d19d76675 | ||
|
|
919f267f7f | ||
|
|
7e0566cc12 | ||
|
|
046caa7632 | ||
|
|
4968c7a8a1 | ||
|
|
4a3e4df2bd | ||
|
|
3a111cf3af | ||
|
|
58df38f74e | ||
|
|
a5f16f0c52 | ||
|
|
ba7a51931d | ||
|
|
b32c2937ae | ||
|
|
017961e892 | ||
|
|
b8a1853692 | ||
|
|
58fe0c79d8 | ||
|
|
7cec2e50d5 | ||
|
|
7c1f560ae2 | ||
|
|
98ac6a06f2 | ||
|
|
4385fc75d0 | ||
|
|
274ebe7a52 | ||
|
|
1a5fb207ae | ||
|
|
fbe917d461 | ||
|
|
5c23e93547 | ||
|
|
cbc5bff1c0 | ||
|
|
d89bf9b635 | ||
|
|
83b2932e02 | ||
|
|
51eb9de9d0 | ||
|
|
a204e422a5 | ||
|
|
e5a215cbd6 | ||
|
|
a3e76334a6 | ||
|
|
b4fb0955a6 | ||
|
|
b2137f2746 | ||
|
|
cacfe67fe1 | ||
|
|
efe15cf2f0 | ||
|
|
a62a4ae02b | ||
|
|
54598f92c7 | ||
|
|
0f998615ad | ||
|
|
2b931dc7a2 | ||
|
|
4c33b99d39 | ||
|
|
e4419204cb | ||
|
|
23bbdc63ba | ||
|
|
adac5690b7 | ||
|
|
a232c32756 | ||
|
|
38cc0a7174 | ||
|
|
725a7e3121 | ||
|
|
e882356264 | ||
|
|
00dcc14df1 | ||
|
|
435a2631c9 | ||
|
|
270e93f6f0 | ||
|
|
9a8323cbb7 | ||
|
|
593c249bbd | ||
|
|
ccb47db3bb | ||
|
|
82d6ad0ef5 | ||
|
|
3c1a6bb8b1 | ||
|
|
00ff47855e | ||
|
|
45c5ec7091 | ||
|
|
4d78e0c63e | ||
|
|
aff2d21bad | ||
|
|
c82b1fc2c9 | ||
|
|
ca0b7a6b06 | ||
|
|
d31be357e5 | ||
|
|
a529a59abf | ||
|
|
cf656e9c21 | ||
|
|
7f1a15b6ba | ||
|
|
f3f9d0dfb7 | ||
|
|
2f26a32f96 | ||
|
|
6a571262fd | ||
|
|
dbdd799696 | ||
|
|
d83fa90606 | ||
|
|
522a5b3c5f | ||
|
|
3362f93964 | ||
|
|
af65c9d5d4 | ||
|
|
f0cdcd90ba | ||
|
|
19e3c97589 | ||
|
|
811ee4d26b | ||
|
|
c847988e10 | ||
|
|
1bb2e8c237 | ||
|
|
77ab3b8d3a | ||
|
|
87d8671995 | ||
|
|
ec4a70ad35 | ||
|
|
f0796055cc | ||
|
|
93ebd66853 | ||
|
|
965564853b | ||
|
|
6e523ed960 | ||
|
|
2d0257562f | ||
|
|
0b8cb3b6e1 | ||
|
|
50e00a3a89 | ||
|
|
1525d5ff16 | ||
|
|
427ad698fe | ||
|
|
94c121bcf7 | ||
|
|
13f0ef30c0 | ||
|
|
abc018e0d8 | ||
|
|
abe1ea78d0 | ||
|
|
229c809a4b | ||
|
|
6eabbe943b | ||
|
|
1b125df572 | ||
|
|
2ce0b72c44 | ||
|
|
8384b8b46b | ||
|
|
7c0bf2c015 | ||
|
|
6fee2f5a0d | ||
|
|
f7ad9e71e0 | ||
|
|
6880ebcbc2 | ||
|
|
04459ae68d | ||
|
|
23440d020f | ||
|
|
ab817c0962 | ||
|
|
6c391a7580 | ||
|
|
0c9447a94a | ||
|
|
b0aacda655 | ||
|
|
d5b2160c64 | ||
|
|
627f122b3f | ||
|
|
48f2a1ec3c | ||
|
|
99519090fb | ||
|
|
85bfde68a1 | ||
|
|
3e84a2f30f | ||
|
|
aa016bf1ac | ||
|
|
e8b0ecfede | ||
|
|
5a780dfde3 | ||
|
|
2c80b6f62d | ||
|
|
0ad64329f6 | ||
|
|
ace2b7fea4 | ||
|
|
f83cfc42b1 | ||
|
|
7785a06441 | ||
|
|
cfe72922ca | ||
|
|
8fc6c8107a | ||
|
|
06123f956a | ||
|
|
ed1cbe32ff | ||
|
|
6593eab9a0 | ||
|
|
6e0ae83685 | ||
|
|
40b651d661 | ||
|
|
3cc9bf7052 | ||
|
|
f4776626e4 | ||
|
|
1b497d31ff | ||
|
|
d4790beb24 | ||
|
|
60b1e284f3 | ||
|
|
8485ff8c76 | ||
|
|
8159f701f4 | ||
|
|
d794ff8bc9 | ||
|
|
26dccfe9a7 | ||
|
|
da8dc4ea03 | ||
|
|
b0c80a7a91 | ||
|
|
34ae76ab7f | ||
|
|
f4a33c5bfb | ||
|
|
7b198545bb | ||
|
|
ed6085d07c | ||
|
|
11a4d7696e | ||
|
|
c8befa5b9a | ||
|
|
422e86e0da | ||
|
|
0b0dbcb5d8 | ||
|
|
2ca0726cd7 | ||
|
|
41e2fd76d1 | ||
|
|
68ac16e5fb | ||
|
|
fa7f340cd3 | ||
|
|
ea59950b2c | ||
|
|
b825b36b60 | ||
|
|
6be41fec6d | ||
|
|
bafc8c88d5 | ||
|
|
8d3ec1afd0 | ||
|
|
306344e9c1 | ||
|
|
eb121c922b | ||
|
|
ec017bd1ae | ||
|
|
16ea2a0104 | ||
|
|
c88bcc1a3c | ||
|
|
57b1523d96 | ||
|
|
c794039b9d | ||
|
|
0176bda584 | ||
|
|
b7f9257b3c | ||
|
|
ad1caca45e | ||
|
|
51167e19fe | ||
|
|
b373b7936a | ||
|
|
07cda695bb | ||
|
|
671042cd20 | ||
|
|
d24a757609 | ||
|
|
0be5abceb9 | ||
|
|
f1475463fc | ||
|
|
0ed5acd6c6 | ||
|
|
dd7f8aac8d | ||
|
|
ba0d677c15 | ||
|
|
b504899714 | ||
|
|
065979e2e1 | ||
|
|
8f8119509d | ||
|
|
6b69ed2174 | ||
|
|
01cda8a130 | ||
|
|
8ad1c58f9e | ||
|
|
e9504b7010 | ||
|
|
9f3551f43b | ||
|
|
26a872be94 | ||
|
|
54067db386 | ||
|
|
34a1896ea8 | ||
|
|
67d9762773 | ||
|
|
6e932a24d2 | ||
|
|
47daf8e7f7 | ||
|
|
9d4b16d03f | ||
|
|
673b85f129 | ||
|
|
da7864b28c | ||
|
|
6fe68dbc7a | ||
|
|
d03b32d249 | ||
|
|
3982303fcc | ||
|
|
aa06a3c111 | ||
|
|
dc8eb317dd | ||
|
|
8402b9ea53 | ||
|
|
9186fa5551 | ||
|
|
d061bafaa2 | ||
|
|
ba89ad7450 | ||
|
|
b73ebb9cf5 | ||
|
|
49d84fccde | ||
|
|
b0b71910a5 | ||
|
|
bf83aed43b | ||
|
|
1a0b0e748c | ||
|
|
0912043248 | ||
|
|
cfbcd9faa8 | ||
|
|
5df2d3db33 | ||
|
|
c654ca4506 | ||
|
|
c780a7471b | ||
|
|
095a3ef28c | ||
|
|
574b464a3c | ||
|
|
176079b678 | ||
|
|
8db7756d2e | ||
|
|
7789038409 | ||
|
|
53abf506fc | ||
|
|
183bae0825 | ||
|
|
c591954fc3 | ||
|
|
65828e0e36 | ||
|
|
e5d8f83dbf | ||
|
|
c896f58bbd | ||
|
|
3f0cc56665 | ||
|
|
50c7dd7d48 | ||
|
|
eb89d45937 | ||
|
|
cb31ce3049 | ||
|
|
ec90bb2e8a | ||
|
|
5cfae1ea75 | ||
|
|
4b827adfee | ||
|
|
c0a29c4c74 | ||
|
|
9835813679 | ||
|
|
c7a99769ce | ||
|
|
19814ecf87 | ||
|
|
ebd4016fb3 | ||
|
|
960e918e61 | ||
|
|
2cf21fd0ad | ||
|
|
15e3178721 | ||
|
|
052aa8afe2 | ||
|
|
699b886d68 | ||
|
|
3634cb712d | ||
|
|
f98513dfc2 | ||
|
|
7ce75bdb52 | ||
|
|
97a0a707d7 | ||
|
|
b243c522b1 | ||
|
|
1018cca4eb | ||
|
|
1e0c701733 | ||
|
|
247f3ac2c4 | ||
|
|
050ed718e5 | ||
|
|
9b3d294464 | ||
|
|
db0029c32b | ||
|
|
1a13d32452 | ||
|
|
6892e511fe | ||
|
|
10b9f2224f | ||
|
|
ccef2485b2 | ||
|
|
8c07fcca24 | ||
|
|
cf2d8d47e6 | ||
|
|
7685fd03c6 | ||
|
|
6db6e858c4 | ||
|
|
661bf33982 | ||
|
|
f8d7daa14f | ||
|
|
f8d293493f | ||
|
|
0a82229ac1 | ||
|
|
025f6d9524 | ||
|
|
c34bb7912c | ||
|
|
9da5ef4743 | ||
|
|
0fe05ce07d | ||
|
|
1549998a19 | ||
|
|
e185b87195 | ||
|
|
63e5f343a3 | ||
|
|
81dde53f5a | ||
|
|
8488b9053d | ||
|
|
ed376e475d | ||
|
|
24d3ba8680 | ||
|
|
13b69a14d8 | ||
|
|
8638f7303a | ||
|
|
3c57c461b5 | ||
|
|
30728879be | ||
|
|
edd447527d | ||
|
|
40c8b7db76 | ||
|
|
1609be6d49 | ||
|
|
1917349c4a | ||
|
|
cd14dea021 | ||
|
|
77acc999cd | ||
|
|
1279f040da | ||
|
|
0f71b0a408 | ||
|
|
ada09f1928 | ||
|
|
839fb4ff58 | ||
|
|
24d077fbce | ||
|
|
16f8c025cd | ||
|
|
b5a75c3250 | ||
|
|
6fe082a5d8 | ||
|
|
0e92deddc4 | ||
|
|
fa2c831ef3 | ||
|
|
2916990077 | ||
|
|
ef6716625e | ||
|
|
a138cea3ed | ||
|
|
1387d88043 | ||
|
|
9deb2d3732 | ||
|
|
a95318f1ab | ||
|
|
5ea6c0f525 | ||
|
|
c74cfa3c5d | ||
|
|
5cce05fc46 | ||
|
|
c2838d78a2 | ||
|
|
ba2f2f8840 | ||
|
|
ebd4fbd1d2 | ||
|
|
b1d8d1081c | ||
|
|
c579fb521e | ||
|
|
fc28375e42 | ||
|
|
f89bb3e125 | ||
|
|
3628b61d75 | ||
|
|
0bddadea35 | ||
|
|
cfc5ecc59c | ||
|
|
72006334f5 | ||
|
|
94a3883880 | ||
|
|
84e7bdb2b1 | ||
|
|
b7cb9aa938 | ||
|
|
259007954b | ||
|
|
a51a6ab489 | ||
|
|
e57f08f46e | ||
|
|
2e25c4c0a3 | ||
|
|
bc593695f9 | ||
|
|
9811564f70 | ||
|
|
32e4daaeba | ||
|
|
aaff13a079 | ||
|
|
241030f916 | ||
|
|
2afa079b6a | ||
|
|
637c48905a | ||
|
|
43098a07e7 | ||
|
|
c37ece9007 | ||
|
|
aaebd8a5cf | ||
|
|
6f70df0852 | ||
|
|
975c4dd03c | ||
|
|
495ace2423 | ||
|
|
b651050dd0 | ||
|
|
eb03a56dfc | ||
|
|
13193d209e | ||
|
|
bcb8824e2c | ||
|
|
1e3cf9fa78 | ||
|
|
b1b516e9c4 | ||
|
|
0e1da5ef28 | ||
|
|
4df666e964 | ||
|
|
0729f2eb59 | ||
|
|
c79474a57c | ||
|
|
ec9dd06d38 | ||
|
|
920d140e40 | ||
|
|
b163b1b685 | ||
|
|
8c28017239 | ||
|
|
e1e66f3082 | ||
|
|
c4aefe9396 | ||
|
|
7674a9eaee | ||
|
|
76c295a60b | ||
|
|
d80af64798 | ||
|
|
ccc51664f4 | ||
|
|
f66d6e4f97 | ||
|
|
67f3d1584c | ||
|
|
c94aef8845 | ||
|
|
4eb6c6fb5b | ||
|
|
8e15846ec5 | ||
|
|
ff55e768c9 | ||
|
|
557bbff36c | ||
|
|
946367aa19 | ||
|
|
2bb8afa9e0 | ||
|
|
42e0cd07ac | ||
|
|
307ac852a6 | ||
|
|
ffecd03cb2 | ||
|
|
0f037651f3 | ||
|
|
77797741a5 | ||
|
|
a19a6317ec | ||
|
|
85e5b20ede | ||
|
|
f34a110d5b | ||
|
|
f54b2ac81a | ||
|
|
afbdb94bb3 | ||
|
|
e85beb91dc | ||
|
|
2f2ef09c92 | ||
|
|
a5641c8144 | ||
|
|
4391c9147b | ||
|
|
308a3e23ad | ||
|
|
0ac7b420f2 | ||
|
|
32abbb9371 | ||
|
|
b74e5ca254 | ||
|
|
9b3e60f5ce | ||
|
|
f0cff687aa | ||
|
|
73fea6a197 | ||
|
|
7e3dbf51fa | ||
|
|
0983fa11be | ||
|
|
d5dc0d4d5b | ||
|
|
e9fa188d42 | ||
|
|
fd3721e050 | ||
|
|
d61d540655 | ||
|
|
e1c049bece | ||
|
|
e498077e6a | ||
|
|
057a92f92c | ||
|
|
cce2191eb6 | ||
|
|
00b5aaeb26 | ||
|
|
91580ed4a3 | ||
|
|
06f78bf28f | ||
|
|
502ad1a02a | ||
|
|
49582c3591 | ||
|
|
f1413c7717 | ||
|
|
55ece141e8 | ||
|
|
519f82f1e7 | ||
|
|
930e059a16 | ||
|
|
82a1b05f28 | ||
|
|
b467444ad3 | ||
|
|
c7e6d9d474 | ||
|
|
d6876b995d | ||
|
|
61bb4944fa | ||
|
|
9f761822e1 | ||
|
|
3cee3de94f | ||
|
|
38432a73e7 | ||
|
|
71b71f2d65 | ||
|
|
d400952acc | ||
|
|
43d28a8595 | ||
|
|
17505673d6 | ||
|
|
0fa3415cc0 | ||
|
|
b31a5fb421 | ||
|
|
eb13def609 | ||
|
|
a721a19137 | ||
|
|
25a75f1876 | ||
|
|
b31a174ac6 | ||
|
|
612158277e | ||
|
|
c60dd05e33 | ||
|
|
e5d7ebd978 | ||
|
|
0645bc6acc | ||
|
|
d01ca92120 | ||
|
|
61c963a8ae | ||
|
|
e83c69d968 | ||
|
|
dfdceb1908 | ||
|
|
36be369901 | ||
|
|
b3673e93d7 | ||
|
|
985ef4518f | ||
|
|
096f033954 | ||
|
|
92f4c0c001 | ||
|
|
10f2c105b1 | ||
|
|
34e343ec66 | ||
|
|
aec56c63be | ||
|
|
c6d6e500fb | ||
|
|
a3c1640fb7 | ||
|
|
0a695dd442 | ||
|
|
64c960041b | ||
|
|
f3048eb574 | ||
|
|
48f38b8940 | ||
|
|
a535c6917e | ||
|
|
41792fdb5f | ||
|
|
3ec231f0ca | ||
|
|
0432514ccf | ||
|
|
a9baae4e98 | ||
|
|
0e44abe741 | ||
|
|
40e44e6dd7 | ||
|
|
02413d165a | ||
|
|
7d596d0840 | ||
|
|
2e0e5cfb5e | ||
|
|
96e34b969c | ||
|
|
2f6be9a389 | ||
|
|
2294cdea4f | ||
|
|
8ce13875a3 | ||
|
|
7ac990119a | ||
|
|
43b643db6c | ||
|
|
9da74cb657 | ||
|
|
2680a9c373 | ||
|
|
f908b68bed | ||
|
|
71f79530be | ||
|
|
e72390bfc4 | ||
|
|
dc83cac5b4 | ||
|
|
5a86ba08f4 | ||
|
|
e421d3689a | ||
|
|
dfeb11a2ab | ||
|
|
66f9ff29e4 | ||
|
|
62c2772982 | ||
|
|
9e7619844f | ||
|
|
6c2e7f4c2c | ||
|
|
08eee147b3 | ||
|
|
43b4508b2c | ||
|
|
dade021eaa | ||
|
|
0041e3fd45 | ||
|
|
d031e5735a | ||
|
|
e64a25bd40 | ||
|
|
d7054e61f0 | ||
|
|
6c1fb7ddca | ||
|
|
7fab15abd4 | ||
|
|
6571b0d88e | ||
|
|
ee83d3bcec | ||
|
|
542ea68824 | ||
|
|
2a930ff702 | ||
|
|
6f7e123b1a | ||
|
|
4196bb627e | ||
|
|
2cbe19af7c | ||
|
|
2ccf83c0aa | ||
|
|
cfbbff1bdf | ||
|
|
f10baa3fcd | ||
|
|
34d1d27c56 | ||
|
|
cc08c090d1 | ||
|
|
caa4efdfa5 | ||
|
|
503b2bc896 | ||
|
|
542ddb9afd | ||
|
|
6214ea7a01 | ||
|
|
3b42f1f709 | ||
|
|
ab75015099 | ||
|
|
e2eac0bd96 | ||
|
|
65c23d9e9f | ||
|
|
1322152391 | ||
|
|
b71e63a540 | ||
|
|
41539387a5 | ||
|
|
22b4c3578b | ||
|
|
5535997356 | ||
|
|
3e2a312f51 | ||
|
|
aad68a5503 | ||
|
|
7f54ac9b3a | ||
|
|
fb9cf93470 | ||
|
|
d1ada7a8aa | ||
|
|
9ffe4a6839 | ||
|
|
f6dc1cb50f | ||
|
|
a024916e66 | ||
|
|
73a1d37785 | ||
|
|
62206bc0e7 | ||
|
|
57b96fdd07 | ||
|
|
de0d7c907d | ||
|
|
8449bc3418 | ||
|
|
f81c275631 | ||
|
|
949bfaa0b4 | ||
|
|
e1fb9f480b | ||
|
|
caa4577cd1 | ||
|
|
43b82f2c53 | ||
|
|
c203f8e8c0 | ||
|
|
75996a618c | ||
|
|
ed24541de6 | ||
|
|
22c8aa0eb8 | ||
|
|
0590c153ef | ||
|
|
c42ae8a55b | ||
|
|
3632d97140 | ||
|
|
e9fc2aa46c | ||
|
|
20590e6823 | ||
|
|
8bb0b4f010 | ||
|
|
ab2318ff9d | ||
|
|
5dcb113996 | ||
|
|
68fb61a5b2 | ||
|
|
2f37e3abf2 | ||
|
|
aecf5b0b18 | ||
|
|
224d389522 | ||
|
|
52054571ab | ||
|
|
ddd7ce2f12 | ||
|
|
0b35d8e2ec | ||
|
|
47a4a1f2df | ||
|
|
486b28e4d2 | ||
|
|
2ce766b3bf | ||
|
|
2deeeab509 | ||
|
|
e93ef301ce | ||
|
|
c7b5aa6e05 | ||
|
|
0edb4edfd0 | ||
|
|
78448b4388 | ||
|
|
969bcf4154 | ||
|
|
f3f0e3ba03 | ||
|
|
aa2734477a | ||
|
|
d0eb2a0ce8 | ||
|
|
a4c8302c55 | ||
|
|
5d4897bd50 | ||
|
|
49d8276033 | ||
|
|
b7fcc68b55 | ||
|
|
4096fc28b8 | ||
|
|
e6eb4dd94f | ||
|
|
29424eb6b3 | ||
|
|
d1e3e5ed20 | ||
|
|
828c3dcd7a | ||
|
|
17558a4bd5 | ||
|
|
304822fb8c | ||
|
|
9fa350d906 | ||
|
|
c62ba223b6 | ||
|
|
4447bbd016 | ||
|
|
98696a29f8 | ||
|
|
84514a1f6f | ||
|
|
c21ea85ec9 | ||
|
|
cec7872f48 | ||
|
|
ce866e8222 | ||
|
|
91b3abae4a | ||
|
|
6e6dd260da | ||
|
|
2d3493c3d5 | ||
|
|
c4449fdfb8 | ||
|
|
bf8f517b49 | ||
|
|
b9822db5bf | ||
|
|
2ebcec4bbe | ||
|
|
68961251a5 | ||
|
|
0f5cc33002 | ||
|
|
e59178dae7 |
14
.github/ISSUE_TEMPLATE.md
vendored
14
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,14 +0,0 @@
|
||||
### If this is a support request:
|
||||
|
||||
**Please attempt to solve the problem on your own before opening an issue.**
|
||||
Between old issues, StackOverflow, and Google, you should be able to find
|
||||
solutions to most of the common problems.
|
||||
|
||||
Include at least:
|
||||
1. Steps to reproduce the issue (e.g. the command you ran)
|
||||
2. The unexpected behavior that occurred (e.g. error messages or screenshots)
|
||||
3. The environment (e.g. operating system and version of manim)
|
||||
|
||||
|
||||
### If this is a feature request:
|
||||
Include the motivation for making this change.
|
||||
20
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Describe the bug
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Code**:
|
||||
<!-- The code you run which reflect the bug. -->
|
||||
|
||||
**Wrong display or Error traceback**:
|
||||
<!-- the wrong display result of the code you run, or the error Traceback -->
|
||||
|
||||
### Additional context
|
||||
<!-- Add any other context about the problem here. -->
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Ask A Question
|
||||
url: https://github.com/3b1b/manim/discussions/categories/q-a
|
||||
about: Please ask questions you encountered here.
|
||||
23
.github/ISSUE_TEMPLATE/error-when-using.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/error-when-using.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Error when using
|
||||
about: The error you encountered while using manim
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Describe the error
|
||||
<!-- A clear and concise description of what you want to make. -->
|
||||
|
||||
### Code and Error
|
||||
**Code**:
|
||||
<!-- The code you run -->
|
||||
|
||||
**Error**:
|
||||
<!-- The error traceback you get when run your code -->
|
||||
|
||||
### Environment
|
||||
**OS System**:
|
||||
**manim version**: master <!-- make sure you are using the latest version of master branch -->
|
||||
**python version**:
|
||||
23
.github/PULL_REQUEST_TEMPLATE.md
vendored
23
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,9 +1,18 @@
|
||||
Thanks for contributing to manim!
|
||||
<!-- Thanks for contributing to manim!
|
||||
Please ensure that your pull request works with the latest version of manim.
|
||||
-->
|
||||
|
||||
**Please ensure that your pull request works with the latest version of manim.**
|
||||
You should also include:
|
||||
## Motivation
|
||||
<!-- Outline your motivation: In what way do your changes improve the library? -->
|
||||
|
||||
1. The motivation for making this change (or link the relevant issues)
|
||||
2. How you tested the new behavior (e.g. a minimal working example, before/after
|
||||
screenshots, gifs, commands, etc.) This is rather informal at the moment, but
|
||||
the goal is to show us how you know the pull request works as intended.
|
||||
## Proposed changes
|
||||
<!-- What you changed in those files -->
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
## Test
|
||||
<!-- How do you test your changes -->
|
||||
**Code**:
|
||||
|
||||
**Result**:
|
||||
40
.github/workflows/docs.yml
vendored
Normal file
40
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
name: build up document and deploy
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Install sphinx and manim env
|
||||
run: |
|
||||
pip3 install --upgrade pip
|
||||
sudo apt install python3-setuptools libpango1.0-dev
|
||||
pip3 install -r docs/requirements.txt
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
- name: Build document with Sphinx
|
||||
run: |
|
||||
cd docs
|
||||
export PATH="$PATH:/home/runner/.local/bin"
|
||||
export SPHINXBUILD="python3 -m sphinx"
|
||||
make html
|
||||
|
||||
- name: Deploy to GitHub pages
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||
with:
|
||||
ACCESS_TOKEN: ${{ secrets.DOC_DEPLOY_TOKEN }}
|
||||
BRANCH: gh-pages
|
||||
FOLDER: docs/build/html
|
||||
30
.github/workflows/publish.yml
vendored
Normal file
30
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Upload Python Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel twine build
|
||||
|
||||
- name: Build and publish
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||
run: |
|
||||
python -m build
|
||||
twine upload dist/*
|
||||
176
.gitignore
vendored
176
.gitignore
vendored
@@ -1,26 +1,152 @@
|
||||
*.pyc
|
||||
*.bak
|
||||
.DS_Store
|
||||
homeless.py
|
||||
playground.py
|
||||
cairo_test.py
|
||||
mayavi_test.py
|
||||
random_scenes/
|
||||
files/
|
||||
ben_playground.py
|
||||
ben_cairo_test.py
|
||||
.floo
|
||||
.flooignore
|
||||
.vscode
|
||||
*.xml
|
||||
*.iml
|
||||
media
|
||||
manim.sublime-project
|
||||
manim.sublime-workspace
|
||||
.eggs/
|
||||
build/
|
||||
dist/
|
||||
manim.egg-info/
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||
|
||||
primes.py
|
||||
/media_dir.txt
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
manimlib.egg-info/
|
||||
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
pytestdebug.log
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
doc/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
pythonenv*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# profiling data
|
||||
.prof
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python
|
||||
# Custom exclusions:
|
||||
.DS_Store
|
||||
|
||||
# For manim
|
||||
/videos
|
||||
/custom_config.yml
|
||||
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
||||
language: python
|
||||
sudo: required # required for Python 3.7 (travis-ci/travis-ci#9069)
|
||||
dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069)
|
||||
python: "3.7"
|
||||
cache: pip
|
||||
|
||||
install:
|
||||
- pip install --upgrade pip
|
||||
- pip install -r requirements.txt
|
||||
- pip install flake8
|
||||
before_script:
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
- flake8 manimlib/ --count --select=E9,F63,F72,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
- flake8 manimlib/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
script:
|
||||
- python setup.py test
|
||||
- python setup.py bdist_wheel
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: eulertour
|
||||
on:
|
||||
tags: true
|
||||
password:
|
||||
secure: j5M2hiJo9kDWJhl0/iSuIQmfd2G2O1Qoc455AkUPMCheAcALnX9xJgFsYBmqfgOXTCtUCQf52XGdOIG4o4s5TY340NZ9eLKI9cWae+sTeSrDCkdwChUilm3D0jQf1FWPUf9ywScwGi20m0sRtzxEJyTuX+JMFd7PIa8bFoDXWPtEjoFOOJrfBusMsANzrI+j+vIMdJ48lc1J8UsQdZapwusTrYU9s12JLhKBPLavmaDKf0HDAJdEhFQ9SaINdkiW/QY8qbfJ/MVu5jHai168zXjD/IaswxoKqCO1G+fWlOq3KwVhG7gI7rwhnnuF+wcA7yLAaMdo0CjO2V7z15S6cG721V2Il2IIh1jq0F8irSH1ZOLOkv/fFk9hkSUQyEU0i8k4m1wE9L47a6GP/66+b+gI91PGfxBOqq4gE/1BdZJqceh0qc13KpcehtYrQwR05bSw0Ye5OoTkqAnCeON0B0Ur4ejfHd3TzkjgB06fw76cZtjAK8f/YjB3KyNCvysOixgzE4tRxlY92yX/tAKZ3iX3yD0MjsinSfwo52N5sIEaCS/FmPRMhJOQBa6ftkfbcUNQBTG9G3b134XXF/LbC4vBloCaTm5VSXagta+oY3SFKQxPAZXx7X+wcFGjqxDjZXG1e66QnA2JJH4aBDsRfSXmUtD8MblwFYdcCJWz+Ck=
|
||||
14
Dockerfile
14
Dockerfile
@@ -1,14 +0,0 @@
|
||||
FROM python:3.7
|
||||
RUN apt-get update \
|
||||
&& apt-get install -qqy --no-install-recommends \
|
||||
apt-utils \
|
||||
ffmpeg \
|
||||
texlive-latex-base \
|
||||
texlive-full \
|
||||
texlive-fonts-extra \
|
||||
sox \
|
||||
libcairo2-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY requirements.txt requirements.txt
|
||||
RUN python -m pip install -r requirements.txt && rm requirements.txt
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
@@ -1,11 +1,6 @@
|
||||
All files of this project under the directories active_projects and old_projects
|
||||
are copyright 3Blue1Brown LLC and used by permission for this project only.
|
||||
|
||||
Any other file of this project is available under the MIT license as follow:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 3Blue1Brown LLC
|
||||
Copyright (c) 2020 3Blue1Brown LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
@@ -0,0 +1,2 @@
|
||||
graft manimlib
|
||||
recursive-exclude manimlib *.pyc *.DS_Store
|
||||
162
README.md
162
README.md
@@ -1,137 +1,117 @@
|
||||
<img src="logo/cropped.png"/>
|
||||
<p align="center">
|
||||
<a href="https://github.com/3b1b/manim">
|
||||
<img src="https://raw.githubusercontent.com/3b1b/manim/master/logo/cropped.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[](https://manim.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://travis-ci.org/3b1b/manim)
|
||||
[](https://pypi.org/project/manimgl/)
|
||||
[](http://choosealicense.com/licenses/mit/)
|
||||
[](https://www.reddit.com/r/manim/)
|
||||
[](https://discord.com/invite/bYCyhM9Kz2)
|
||||
[](https://3b1b.github.io/manim/)
|
||||
|
||||
Manim is an animation engine for explanatory math videos. It's used to create precise animations programmatically, as seen in the videos at [3Blue1Brown](https://www.3blue1brown.com/).
|
||||
Manim is an engine for precise programmatic animations, designed for creating explanatory math videos.
|
||||
|
||||
Note, there are two versions of manim. This repository began as a personal project by the author of [3Blue1Brown](https://www.3blue1brown.com/) for the purpose of animating those videos, with video-specific code available [here](https://github.com/3b1b/videos). In 2020 a group of developers forked it into what is now the [community edition](https://github.com/ManimCommunity/manim/), with a goal of being more stable, better tested, quicker to respond to community contributions, and all around friendlier to get started with. See [this page](https://docs.manim.community/en/stable/installation/versions.html?highlight=OpenGL#which-version-to-use) for more details.
|
||||
|
||||
## Installation
|
||||
Manim runs on python 3.7. You can install it from PyPI via pip
|
||||
> **WARNING:** These instructions are for ManimGL _only_. Trying to use these instructions to install [ManimCommunity/manim](https://github.com/ManimCommunity/manim) or instructions there to install this version will cause problems. You should first decide which version you wish to install, then only follow the instructions for your desired version.
|
||||
>
|
||||
> **Note**: To install manim directly through pip, please pay attention to the name of the installed package. This repository is ManimGL of 3b1b. The package name is `manimgl` instead of `manim` or `manimlib`. Please use `pip install manimgl` to install the version in this repository.
|
||||
|
||||
```sh
|
||||
pip3 install manimlib
|
||||
```
|
||||
Manim runs on Python 3.6 or higher (Python 3.8 is recommended).
|
||||
|
||||
System requirements are [cairo](https://www.cairographics.org), [ffmpeg](https://www.ffmpeg.org), [sox](http://sox.sourceforge.net), [latex](https://www.latex-project.org) (optional, if you want to use LaTeX).
|
||||
System requirements are [FFmpeg](https://ffmpeg.org/), [OpenGL](https://www.opengl.org/) and [LaTeX](https://www.latex-project.org) (optional, if you want to use LaTeX).
|
||||
For Linux, [Pango](https://pango.gnome.org) along with its development headers are required. See instruction [here](https://github.com/ManimCommunity/ManimPango#building).
|
||||
|
||||
You can now use it via the `manim` command. For example:
|
||||
|
||||
```sh
|
||||
manim my_project.py MyScene
|
||||
```
|
||||
|
||||
For more options, take a look at the “Using manim“ sections further below.
|
||||
|
||||
### Directly
|
||||
|
||||
```sh
|
||||
# Install manimgl
|
||||
pip install manimgl
|
||||
|
||||
# Try it out
|
||||
manimgl
|
||||
```
|
||||
|
||||
For more options, take a look at the [Using manim](#using-manim) sections further below.
|
||||
|
||||
If you want to hack on manimlib itself, clone this repository and in that directory execute:
|
||||
|
||||
```sh
|
||||
# Install python requirements
|
||||
python3 -m pip install -r requirements.txt
|
||||
# Install manimgl
|
||||
pip install -e .
|
||||
|
||||
# Try it out
|
||||
python3 -m manim example_scenes.py SquareToCircle -pl
|
||||
manimgl example_scenes.py OpeningManimExample
|
||||
# or
|
||||
manim-render example_scenes.py OpeningManimExample
|
||||
```
|
||||
|
||||
### Directly (Windows)
|
||||
|
||||
1. [Install FFmpeg](https://www.wikihow.com/Install-FFmpeg-on-Windows).
|
||||
2. Install Cairo. Download the wheel from https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo. For most users, ``pycairo‑1.18.0‑cp37‑cp37m‑win32.whl`` will do fine.
|
||||
```sh
|
||||
pip3 install C:\path\to\wheel\pycairo‑1.18.0‑cp37‑cp37m‑win32.whl
|
||||
```
|
||||
3. Install a LaTeX distribution. [MiKTeX](https://miktex.org/download) is recommended.
|
||||
|
||||
4. [Install SoX](https://sourceforge.net/projects/sox/files/sox/).
|
||||
|
||||
5. Install the remaining Python packages. Make sure that ``pycairo==1.17.1`` is changed to ``pycairo==1.18.0`` in requirements.txt.
|
||||
2. Install a LaTeX distribution. [MiKTeX](https://miktex.org/download) is recommended.
|
||||
3. Install the remaining Python packages.
|
||||
```sh
|
||||
git clone https://github.com/3b1b/manim.git
|
||||
cd manim
|
||||
pip3 install -r requirements.txt
|
||||
python3 manim.py example_scenes.py SquareToCircle -pl
|
||||
pip install -e .
|
||||
manimgl example_scenes.py OpeningManimExample
|
||||
```
|
||||
|
||||
### Mac OSX
|
||||
|
||||
1. Install FFmpeg, LaTeX in terminal using homebrew.
|
||||
```sh
|
||||
brew install ffmpeg mactex
|
||||
```
|
||||
|
||||
2. Install latest version of manim using these command.
|
||||
```sh
|
||||
git clone https://github.com/3b1b/manim.git
|
||||
cd manim
|
||||
pip install -e .
|
||||
manimgl example_scenes.py OpeningManimExample
|
||||
```
|
||||
|
||||
## Anaconda Install
|
||||
|
||||
* Install sox and latex as above.
|
||||
* Create a conda environment using `conda env create -f environment.yml`
|
||||
* **WINDOWS ONLY** Install `pyreadline` via `pip install pyreadline`.
|
||||
1. Install LaTeX as above.
|
||||
2. Create a conda environment using `conda create -n manim python=3.8`.
|
||||
3. Activate the environment using `conda activate manim`.
|
||||
4. Install manimgl using `pip install -e .`.
|
||||
|
||||
|
||||
### Using `virtualenv` and `virtualenvwrapper`
|
||||
After installing `virtualenv` and `virtualenvwrapper`
|
||||
```sh
|
||||
git clone https://github.com/3b1b/manim.git
|
||||
mkvirtualenv -a manim -r requirements.txt manim
|
||||
python3 -m manim example_scenes.py SquareToCircle -pl
|
||||
```
|
||||
|
||||
### Using Docker
|
||||
Since it's a bit tricky to get all the dependencies set up just right, there is a Dockerfile and Compose file provided in this repo as well as [a premade image on Docker Hub](https://hub.docker.com/r/eulertour/manim/tags/). The Dockerfile contains instructions on how to build a manim image, while the Compose file contains instructions on how to run the image.
|
||||
|
||||
The image does not contain a copy of the repo. This is intentional, as it allows you to either bind mount a repo that you've cloned locally or clone any fork/branch you want. In order to do this with the Compose file, you must set the `MANIM_PATH` environment variable to the absolute path to the manim repo.
|
||||
|
||||
1. [Install Docker](https://www.docker.com/products/overview)
|
||||
2. [Install Docker Compose](https://docs.docker.com/compose/install/)
|
||||
3. Render an animation
|
||||
```sh
|
||||
MANIM_PATH=/absolute/path/to/manim/repo docker-compose run manim example_scenes.py SquareToCircle -l
|
||||
```
|
||||
The first time you execute the above command, Docker will pull the image from Docker Hub and cache it. Any subsequent runs until the image is evicted will use the cached image.
|
||||
Note that the image doesn't have any development tools installed and can't preview animations. Its purpose is building and testing only.
|
||||
|
||||
## Using manim
|
||||
Try running the following:
|
||||
```sh
|
||||
python3 -m manim example_scenes.py SquareToCircle -pl
|
||||
manimgl example_scenes.py OpeningManimExample
|
||||
```
|
||||
The -p is for previewing, meaning the video file will automatically open when it is done rendering.
|
||||
Use -l for a faster rendering at a lower quality.
|
||||
Use -s to skip to the end and just show the final frame.
|
||||
Use -n (number) to skip ahead to the n'th animation of a scene.
|
||||
Use -f to show the file in finder (for osx)
|
||||
This should pop up a window playing a simple scene.
|
||||
|
||||
Set MEDIA_DIR environment variable to determine where image and animation files will be written.
|
||||
Some useful flags include:
|
||||
* `-w` to write the scene to a file
|
||||
* `-o` to write the scene to a file and open the result
|
||||
* `-s` to skip to the end and just show the final frame.
|
||||
* `-so` will save the final frame to an image and show it
|
||||
* `-n <number>` to skip ahead to the `n`'th animation of a scene.
|
||||
* `-f` to make the playback window fullscreen
|
||||
|
||||
Look through the old_projects folder to see the code for previous 3b1b videos. Note, however, that developments are often made to the library without considering backwards compatibility on those old_projects. To run them with a guarantee that they will work, you will have to go back to the commit which complete that project.
|
||||
Take a look at custom_config.yml for further configuration. To add your customization, you can either edit this file, or add another file by the same name "custom_config.yml" to whatever directory you are running manim from. For example [this is the one](https://github.com/3b1b/videos/blob/master/custom_config.yml) for 3blue1brown videos. There you can specify where videos should be output to, where manim should look for image files and sounds you want to read in, and other defaults regarding style and video quality.
|
||||
|
||||
While developing a scene, the `-sp` flags are helpful to just see what things look like at the end without having to generate the full animation. It can also be helpful to use the `-n` flag to skip over some number of animations.
|
||||
Look through the [example scenes](https://3b1b.github.io/manim/getting_started/example_scenes.html) to get a sense of how it is used, and feel free to look through the code behind [3blue1brown videos](https://github.com/3b1b/videos) for a much larger set of example. Note, however, that developments are often made to the library without considering backwards compatibility with those old videos. To run an old project with a guarantee that it will work, you will have to go back to the commit which completed that project.
|
||||
|
||||
### Documentation
|
||||
Documentation is in progress at [manim.readthedocs.io](https://manim.readthedocs.io).
|
||||
Documentation is in progress at [3b1b.github.io/manim](https://3b1b.github.io/manim/). And there is also a Chinese version maintained by [**@manim-kindergarten**](https://manim.org.cn): [docs.manim.org.cn](https://docs.manim.org.cn/) (in Chinese).
|
||||
|
||||
### Walkthrough
|
||||
Todd Zimmerman put together a [tutorial](https://talkingphysics.wordpress.com/2019/01/08/getting-started-animating-with-manim-and-python-3-7/) on getting started with manim, which has been updated to run on python 3.7.
|
||||
|
||||
### Live Streaming
|
||||
To live stream your animations, simply run manim with the `--livestream` option.
|
||||
|
||||
```sh
|
||||
> python -m manim --livestream
|
||||
Writing to media/videos/scene/scene/1080p30/LiveStreamTemp.mp4
|
||||
|
||||
Manim is now running in streaming mode. Stream animations by passing
|
||||
them to manim.play(), e.g.
|
||||
>>> c = Circle()
|
||||
>>> manim.play(ShowCreation(c))
|
||||
|
||||
>>>
|
||||
```
|
||||
|
||||
It is also possible to stream directly to Twitch. To do that simply pass
|
||||
--livestream and --to-twitch to manim and specify the stream key with
|
||||
--with-key. Then when you follow the above example the stream will directly
|
||||
start on your Twitch channel (with no audio support).
|
||||
[manim-kindergarten](https://github.com/manim-kindergarten/) wrote and collected some useful extra classes and some codes of videos in [manim_sandbox repo](https://github.com/manim-kindergarten/manim_sandbox).
|
||||
|
||||
|
||||
## Contributing
|
||||
Is always welcome. In particular, there is a dire need for tests and documentation.
|
||||
Is always welcome. As mentioned above, the [community edition](https://github.com/ManimCommunity/manim) has the most active ecosystem for contributions, with testing and continuous integration, but pull requests are welcome here too. Please explain the motivation for a given change and examples of its effect.
|
||||
|
||||
|
||||
## License
|
||||
All files in the directories active_projects and old_projects, which by and large generate the visuals for 3b1b videos, are copyright 3Blue1Brown.
|
||||
|
||||
The general purpose animation code found in the remainder of the repository, on the other hand, is under the MIT license.
|
||||
This project falls under the MIT license.
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
def get_factors(n):
|
||||
return filter(
|
||||
lambda k: (n % k) == 0,
|
||||
range(1, n)
|
||||
)
|
||||
|
||||
|
||||
class AmicableNumbers(Scene):
|
||||
CONFIG = {
|
||||
"n1": 28,
|
||||
"n2": 284,
|
||||
"colors": [
|
||||
BLUE_C,
|
||||
BLUE_B,
|
||||
BLUE_D,
|
||||
GREY_BROWN,
|
||||
GREEN_C,
|
||||
GREEN_B,
|
||||
GREEN_D,
|
||||
GREY,
|
||||
]
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.show_n1()
|
||||
self.show_n1_factors()
|
||||
self.show_n1_factor_sum()
|
||||
self.show_n2_factors()
|
||||
self.show_n2_factor_sum()
|
||||
|
||||
def show_n1(self):
|
||||
dots = VGroup(*[Dot() for x in range(self.n1)])
|
||||
dots.set_color(BLUE)
|
||||
dots.set_sheen(0.2, UL)
|
||||
dots.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
dots.set_width(FRAME_WIDTH - 1)
|
||||
|
||||
rects = self.get_all_factor_rectangles(dots)
|
||||
rects.rotate(90 * DEGREES)
|
||||
rects.arrange(RIGHT, buff=MED_SMALL_BUFF, aligned_edge=DOWN)
|
||||
for rect in rects:
|
||||
rect.first_col.set_stroke(WHITE, 3)
|
||||
rects.set_height(FRAME_HEIGHT - 1)
|
||||
|
||||
self.add(rects)
|
||||
|
||||
def show_n1_factors(self):
|
||||
pass
|
||||
|
||||
def show_n1_factor_sum(self):
|
||||
pass
|
||||
|
||||
def show_n2_factors(self):
|
||||
pass
|
||||
|
||||
def show_n2_factor_sum(self):
|
||||
pass
|
||||
|
||||
#
|
||||
def show_factors(self, dot_group):
|
||||
pass
|
||||
|
||||
def get_all_factor_rectangles(self, dot_group):
|
||||
n = len(dot_group)
|
||||
factors = get_factors(n)
|
||||
colors = it.cycle(self.colors)
|
||||
result = VGroup()
|
||||
for k, color in zip(factors, colors):
|
||||
group = dot_group.copy()
|
||||
group.set_color(color)
|
||||
group.arrange_in_grid(n_rows=k, buff=SMALL_BUFF)
|
||||
group.first_col = group[::(n // k)]
|
||||
result.add(group)
|
||||
return result
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,283 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class WorkOutNumerically(Scene):
|
||||
CONFIG = {
|
||||
"M1_COLOR": TEAL,
|
||||
"M2_COLOR": PINK,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_question()
|
||||
self.add_example()
|
||||
self.compute_rhs()
|
||||
self.compute_lhs()
|
||||
|
||||
def add_question(self):
|
||||
equation = self.original_equation = TexMobject(
|
||||
"\\det(", "M_1", "M_2", ")", "=",
|
||||
"\\det(", "M_1", ")",
|
||||
"\\det(", "M_2", ")",
|
||||
)
|
||||
equation.set_color_by_tex_to_color_map({
|
||||
"M_1": self.M1_COLOR,
|
||||
"M_2": self.M2_COLOR,
|
||||
})
|
||||
challenge = TextMobject("Explain in one sentence")
|
||||
challenge.set_color(YELLOW)
|
||||
group = VGroup(challenge, equation)
|
||||
group.arrange(DOWN)
|
||||
group.to_edge(UP)
|
||||
|
||||
self.add(equation)
|
||||
self.play(Write(challenge))
|
||||
self.wait()
|
||||
|
||||
def add_example(self):
|
||||
M1 = self.M1 = Matrix([[2, -1], [1, 1]])
|
||||
M1.set_color(self.M1_COLOR)
|
||||
self.M1_copy = M1.copy()
|
||||
M2 = self.M2 = Matrix([[-1, 4], [1, 1]])
|
||||
M2.set_color(self.M2_COLOR)
|
||||
self.M2_copy = M2.copy()
|
||||
eq_parts = TexMobject(
|
||||
"\\det", "\\big(", "\\big)", "=",
|
||||
"\\det", "\\big(", "\\big)",
|
||||
"\\det", "\\big(", "\\big)",
|
||||
)
|
||||
for part in eq_parts.get_parts_by_tex("\\big"):
|
||||
part.scale(2)
|
||||
part.stretch(1.5, 1)
|
||||
i1, i2, i3 = [
|
||||
eq_parts.index_of_part(part) + 1
|
||||
for part in eq_parts.get_parts_by_tex("\\big(")
|
||||
]
|
||||
equation = self.equation_with_numbers = VGroup(*it.chain(
|
||||
eq_parts[:i1], [M1, M2],
|
||||
eq_parts[i1:i2], [self.M1_copy],
|
||||
eq_parts[i2:i3], [self.M2_copy],
|
||||
eq_parts[i3:],
|
||||
))
|
||||
equation.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
eq_parts.get_part_by_tex("=").shift(0.2 * SMALL_BUFF * DOWN)
|
||||
equation.set_width(FRAME_WIDTH - 2 * LARGE_BUFF)
|
||||
equation.next_to(self.original_equation, DOWN, MED_LARGE_BUFF)
|
||||
|
||||
self.play(LaggedStartMap(FadeIn, equation))
|
||||
|
||||
def compute_rhs(self):
|
||||
M1, M2 = self.M1_copy, self.M2_copy
|
||||
|
||||
line1 = VGroup(
|
||||
TexMobject(
|
||||
"\\big(", "2", "\\cdot", "2", "-",
|
||||
"(-1)", "\\cdot", "1", "\\big)"
|
||||
),
|
||||
TexMobject(
|
||||
"\\big(", "-1", "\\cdot", "1", "-",
|
||||
"4", "\\cdot", "1", "\\big)"
|
||||
),
|
||||
)
|
||||
line1.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
line1[0].set_color(self.M1_COLOR)
|
||||
line1[1].set_color(self.M2_COLOR)
|
||||
indices = [1, 3, 5, 7]
|
||||
|
||||
line2 = TexMobject("(3)", "(-5)")
|
||||
line2.match_style(line1)
|
||||
line3 = TexMobject("-15")
|
||||
arrows = [TexMobject("\\downarrow") for x in range(2)]
|
||||
lines = VGroup(line1, arrows[0], line2, arrows[1], line3)
|
||||
lines.arrange(DOWN, buff=MED_SMALL_BUFF)
|
||||
lines.next_to(self.equation_with_numbers, DOWN, buff=MED_LARGE_BUFF)
|
||||
lines.to_edge(RIGHT)
|
||||
|
||||
for matrix, det in zip([M1, M2], line1):
|
||||
numbers = VGroup(*[det[i] for i in indices])
|
||||
numbers_iter = iter(numbers)
|
||||
non_numbers = VGroup(*[m for m in det if m not in numbers])
|
||||
matrix_numbers = VGroup(*[
|
||||
matrix.mob_matrix[i][j].copy()
|
||||
for i, j in ((0, 0), (1, 1), (0, 1), (1, 0))
|
||||
])
|
||||
self.play(
|
||||
LaggedStartMap(FadeIn, non_numbers, run_time=1),
|
||||
LaggedStartMap(
|
||||
ReplacementTransform,
|
||||
matrix_numbers,
|
||||
lambda m: (m, next(numbers_iter)),
|
||||
path_arc=TAU / 6
|
||||
),
|
||||
)
|
||||
self.play(LaggedStartMap(FadeIn, lines[1:], run_time=3))
|
||||
|
||||
def compute_lhs(self):
|
||||
matrix = Matrix([[-3, 7], [0, 5]])
|
||||
matrix.set_color(BLUE)
|
||||
matrix.scale(0.8)
|
||||
empty_det_tex = TexMobject("\\det", "\\big(", "\\big)")
|
||||
empty_det_tex[1:].scale(1.5)
|
||||
empty_det_tex[1:].match_height(matrix, stretch=True)
|
||||
det_tex = VGroup(empty_det_tex[:2], matrix, *empty_det_tex[2:])
|
||||
det_tex.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
|
||||
group = VGroup(
|
||||
det_tex,
|
||||
TexMobject("\\downarrow"),
|
||||
TexMobject("(-3)(5) - (7)(0)").scale(0.8),
|
||||
TexMobject("\\downarrow"),
|
||||
TexMobject("-15"),
|
||||
)
|
||||
group.arrange(DOWN, buff=2 * SMALL_BUFF)
|
||||
# group.set_height(0.4*FRAME_HEIGHT)
|
||||
group.next_to(self.equation_with_numbers, DOWN)
|
||||
group.shift(FRAME_WIDTH * LEFT / 4)
|
||||
|
||||
self.play(FadeIn(empty_det_tex))
|
||||
self.play(*[
|
||||
ReplacementTransform(M.copy(), matrix)
|
||||
for M in (self.M1, self.M2)
|
||||
])
|
||||
self.play(LaggedStartMap(FadeIn, group[1:]))
|
||||
self.wait()
|
||||
|
||||
|
||||
class LetsGoInOneSentence(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.teacher_says(
|
||||
"Here we go, \\\\", "one sentence!"
|
||||
)
|
||||
self.change_all_student_modes("hooray")
|
||||
self.teacher_says(
|
||||
"Or three...", "",
|
||||
target_mode="guilty"
|
||||
)
|
||||
self.change_all_student_modes("sassy")
|
||||
self.wait(4)
|
||||
|
||||
|
||||
class SuccessiveLinearTransformations(LinearTransformationScene):
|
||||
CONFIG = {
|
||||
"matrix_2": [[3, -1], [0, 1]],
|
||||
"matrix_1": [[2, 3], [-1. / 3, 2]],
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.create_product_and_inverse()
|
||||
self.scale_area_successively()
|
||||
self.apply_transformations_successively()
|
||||
self.scale_area_successively(
|
||||
"\\det(M_2)", "\\det(M_1)", "\\det(M_1 M_2)",
|
||||
reset=False
|
||||
)
|
||||
# self.show_det_as_scaling_factor()
|
||||
|
||||
def create_product_and_inverse(self):
|
||||
self.matrix_product = np.dot(self.matrix_1, self.matrix_2)
|
||||
self.matrix_product_inverse = np.linalg.inv(self.matrix_product)
|
||||
|
||||
def scale_area_successively(self, tex2="3", tex1="5", tex_prod="15", reset=True):
|
||||
self.add_unit_square()
|
||||
t1 = "$%s \\, \\cdot $" % tex1
|
||||
t2 = "$%s \\, \\cdot $" % tex2
|
||||
t3 = "Area"
|
||||
areas = VGroup(
|
||||
TextMobject("", "", t3),
|
||||
TextMobject("", t2, t3),
|
||||
TextMobject(t1, t2, t3),
|
||||
)
|
||||
areas.scale(0.8)
|
||||
areas.move_to(self.square)
|
||||
area = areas[0]
|
||||
self.add_moving_mobject(area, areas[1])
|
||||
|
||||
self.play(
|
||||
FadeIn(self.square),
|
||||
Write(area),
|
||||
Animation(self.basis_vectors)
|
||||
)
|
||||
self.apply_matrix(self.matrix_2)
|
||||
self.wait()
|
||||
self.add_moving_mobject(area, areas[2])
|
||||
self.apply_matrix(self.matrix_1)
|
||||
self.wait()
|
||||
|
||||
product = VGroup(area[:2])
|
||||
brace = Brace(product, DOWN, buff=SMALL_BUFF)
|
||||
brace_tex = brace.get_tex(tex_prod, buff=SMALL_BUFF)
|
||||
brace_tex.scale(0.8, about_edge=UP)
|
||||
|
||||
self.play(
|
||||
GrowFromCenter(brace),
|
||||
Write(brace_tex)
|
||||
)
|
||||
self.wait()
|
||||
if reset:
|
||||
self.play(
|
||||
FadeOut(VGroup(self.square, area, brace, brace_tex)),
|
||||
Animation(self.plane),
|
||||
Animation(self.basis_vectors)
|
||||
)
|
||||
self.transformable_mobjects.remove(self.square)
|
||||
self.moving_mobjects = []
|
||||
self.reset_plane()
|
||||
self.wait()
|
||||
|
||||
def apply_transformations_successively(self):
|
||||
M1, M2, all_space = expression = TexMobject(
|
||||
"M_1", "M_2", "\\text{(All 2d space)}"
|
||||
)
|
||||
expression.set_color_by_tex_to_color_map({
|
||||
"M_1": TEAL,
|
||||
"M_2": PINK,
|
||||
})
|
||||
expression.shift(FRAME_WIDTH * LEFT / 4)
|
||||
expression.to_edge(UP)
|
||||
for part in expression:
|
||||
part.add_background_rectangle()
|
||||
part.background_rectangle.stretch(1.05, 0)
|
||||
M1.save_state()
|
||||
M1.move_to(ORIGIN)
|
||||
M1.fade(1)
|
||||
|
||||
# Apply one after the other
|
||||
self.play(
|
||||
FocusOn(M2, run_time=1),
|
||||
FadeIn(VGroup(M2, all_space))
|
||||
)
|
||||
self.add_foreground_mobjects(M2, all_space)
|
||||
self.apply_matrix(self.matrix_2)
|
||||
self.wait()
|
||||
self.play(M1.restore)
|
||||
self.add_foreground_mobjects(M1)
|
||||
self.apply_matrix(self.matrix_1)
|
||||
self.wait()
|
||||
|
||||
# Show full composition
|
||||
rp, lp = parens = TexMobject("()")
|
||||
matrices = VGroup(M1, M2)
|
||||
matrices.generate_target()
|
||||
parens.match_height(matrices)
|
||||
lp.move_to(matrices, RIGHT)
|
||||
matrices.target.next_to(lp, LEFT, SMALL_BUFF)
|
||||
rp.next_to(matrices.target, LEFT, SMALL_BUFF)
|
||||
|
||||
self.reset_plane()
|
||||
self.play(
|
||||
MoveToTarget(matrices),
|
||||
*list(map(GrowFromCenter, parens))
|
||||
)
|
||||
self.apply_matrix(self.matrix_product)
|
||||
self.wait()
|
||||
self.reset_plane()
|
||||
|
||||
def show_det_as_scaling_factor(self):
|
||||
pass
|
||||
|
||||
###
|
||||
|
||||
def reset_plane(self):
|
||||
plane_and_bases = VGroup(self.plane, self.basis_vectors)
|
||||
self.play(FadeOut(plane_and_bases))
|
||||
self.apply_matrix(self.matrix_product_inverse, run_time=0)
|
||||
self.play(FadeIn(plane_and_bases))
|
||||
@@ -1,222 +0,0 @@
|
||||
from fractions import Fraction
|
||||
|
||||
from manimlib.imports import *
|
||||
from functools import reduce
|
||||
|
||||
|
||||
class FractionMobject(VGroup):
|
||||
CONFIG = {
|
||||
"max_height": 1,
|
||||
}
|
||||
|
||||
def __init__(self, fraction, **kwargs):
|
||||
VGroup.__init__(self, **kwargs)
|
||||
numerator = self.numerator = Integer(fraction.numerator)
|
||||
self.add(numerator)
|
||||
if fraction.denominator != 1:
|
||||
denominator = Integer(fraction.denominator)
|
||||
line = TexMobject("/")
|
||||
numerator.next_to(line, LEFT, SMALL_BUFF)
|
||||
denominator.next_to(line, RIGHT, SMALL_BUFF)
|
||||
self.add(numerator, line, denominator)
|
||||
self.set_height(min(self.max_height, self.get_height()))
|
||||
self.value = fraction
|
||||
|
||||
def add_plus_if_needed(self):
|
||||
if self.value > 0:
|
||||
plus = TexMobject("+")
|
||||
plus.next_to(self, LEFT, SMALL_BUFF)
|
||||
plus.match_color(self)
|
||||
self.add_to_back(plus)
|
||||
|
||||
|
||||
class ShowRowReduction(Scene):
|
||||
CONFIG = {
|
||||
"matrices": [
|
||||
[
|
||||
[2, -1, -1],
|
||||
[0, 3, -4],
|
||||
[-3, 2, 1],
|
||||
],
|
||||
[
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1],
|
||||
],
|
||||
# [[1], [2], [3]],
|
||||
],
|
||||
"h_spacing": 2,
|
||||
"extra_h_spacing": 0.5,
|
||||
"v_spacing": 1,
|
||||
"include_separation_lines": True,
|
||||
"changing_row_color": YELLOW,
|
||||
"reference_row_color": BLUE,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.initialize_terms()
|
||||
|
||||
self.apply_row_rescaling(0, Fraction(1, 2))
|
||||
self.add_row_multiple_to_row(2, 0, 3)
|
||||
self.apply_row_rescaling(1, Fraction(1, 3))
|
||||
self.add_row_multiple_to_row(2, 1, Fraction(-1, 2))
|
||||
self.apply_row_rescaling(2, Fraction(6))
|
||||
self.add_row_multiple_to_row(0, 1, Fraction(1, 2))
|
||||
self.add_row_multiple_to_row(0, 2, Fraction(7, 6))
|
||||
self.add_row_multiple_to_row(1, 2, Fraction(4, 3))
|
||||
self.wait()
|
||||
|
||||
def initialize_terms(self):
|
||||
full_matrix = reduce(
|
||||
lambda m, v: np.append(m, v, axis=1),
|
||||
self.matrices
|
||||
)
|
||||
mobject_matrix = np.vectorize(FractionMobject)(full_matrix)
|
||||
rows = self.rows = VGroup(*it.starmap(VGroup, mobject_matrix))
|
||||
for i, row in enumerate(rows):
|
||||
for j, term in enumerate(row):
|
||||
term.move_to(
|
||||
i * self.v_spacing * DOWN +
|
||||
j * self.h_spacing * RIGHT
|
||||
)
|
||||
|
||||
# Visually seaprate distinct parts
|
||||
separation_lines = self.separation_lines = VGroup()
|
||||
lengths = [len(m[0]) for m in self.matrices]
|
||||
for partial_sum in np.cumsum(lengths)[:-1]:
|
||||
VGroup(*mobject_matrix[:, partial_sum:].flatten()).shift(
|
||||
self.extra_h_spacing * RIGHT
|
||||
)
|
||||
c1 = VGroup(*mobject_matrix[:, partial_sum - 1])
|
||||
c2 = VGroup(*mobject_matrix[:, partial_sum])
|
||||
line = DashedLine(c1.get_top(), c1.get_bottom())
|
||||
line.move_to(VGroup(c1, c2))
|
||||
separation_lines.add(line)
|
||||
|
||||
if self.include_separation_lines:
|
||||
group = VGroup(rows, separation_lines)
|
||||
else:
|
||||
group = rows
|
||||
group.center().to_edge(DOWN, buff=2)
|
||||
self.add(group)
|
||||
|
||||
def add_variables(self):
|
||||
# If it is meant to represent a system of equations
|
||||
pass
|
||||
|
||||
def apply_row_rescaling(self, row_index, scale_factor):
|
||||
row = self.rows[row_index]
|
||||
new_row = VGroup()
|
||||
for element in row:
|
||||
target = FractionMobject(element.value * scale_factor)
|
||||
target.move_to(element)
|
||||
new_row.add(target)
|
||||
new_row.set_color(self.changing_row_color)
|
||||
|
||||
label = VGroup(
|
||||
TexMobject("r_%d" % (row_index + 1)),
|
||||
TexMobject("\\rightarrow"),
|
||||
TexMobject("("),
|
||||
FractionMobject(scale_factor),
|
||||
TexMobject(")"),
|
||||
TexMobject("r_%d" % (row_index + 1)),
|
||||
)
|
||||
label.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
label.to_edge(UP)
|
||||
VGroup(label[0], label[-1]).set_color(self.changing_row_color)
|
||||
|
||||
scalar_mob = FractionMobject(scale_factor)
|
||||
scalar_mob.add_to_back(
|
||||
TexMobject("\\times").next_to(scalar_mob, LEFT, SMALL_BUFF)
|
||||
)
|
||||
scalar_mob.scale(0.5)
|
||||
scalar_mob.next_to(row[0], DR, SMALL_BUFF)
|
||||
|
||||
# Do do, fancier illustrations here
|
||||
self.play(
|
||||
FadeIn(label),
|
||||
row.set_color, self.changing_row_color,
|
||||
)
|
||||
self.play(FadeIn(scalar_mob))
|
||||
for elem, new_elem in zip(row, new_row):
|
||||
self.play(scalar_mob.next_to, elem, DR, SMALL_BUFF)
|
||||
self.play(ReplacementTransform(elem, new_elem, path_arc=30 * DEGREES))
|
||||
self.play(FadeOut(scalar_mob))
|
||||
self.play(new_row.set_color, WHITE)
|
||||
self.play(FadeOut(label))
|
||||
self.rows.submobjects[row_index] = new_row
|
||||
|
||||
def add_row_multiple_to_row(self, row1_index, row2_index, scale_factor):
|
||||
row1 = self.rows[row1_index]
|
||||
row2 = self.rows[row2_index]
|
||||
new_row1 = VGroup()
|
||||
scaled_row2 = VGroup()
|
||||
for elem1, elem2 in zip(row1, row2):
|
||||
target = FractionMobject(elem1.value + scale_factor * elem2.value)
|
||||
target.move_to(elem1)
|
||||
new_row1.add(target)
|
||||
|
||||
scaled_term = FractionMobject(scale_factor * elem2.value)
|
||||
scaled_term.move_to(elem2)
|
||||
scaled_row2.add(scaled_term)
|
||||
new_row1.set_color(self.changing_row_color)
|
||||
scaled_row2.set_color(self.reference_row_color)
|
||||
|
||||
for elem1, elem2 in zip(row1, scaled_row2):
|
||||
elem2.add_plus_if_needed()
|
||||
elem2.scale(0.5)
|
||||
elem2.next_to(elem1, UL, buff=SMALL_BUFF)
|
||||
|
||||
label = VGroup(
|
||||
TexMobject("r_%d" % (row1_index + 1)),
|
||||
TexMobject("\\rightarrow"),
|
||||
TexMobject("r_%d" % (row1_index + 1)),
|
||||
TexMobject("+"),
|
||||
TexMobject("("),
|
||||
FractionMobject(scale_factor),
|
||||
TexMobject(")"),
|
||||
TexMobject("r_%d" % (row2_index + 1)),
|
||||
)
|
||||
label.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
label.to_edge(UP)
|
||||
VGroup(label[0], label[2]).set_color(self.changing_row_color)
|
||||
label[-1].set_color(self.reference_row_color)
|
||||
|
||||
self.play(
|
||||
FadeIn(label),
|
||||
row1.set_color, self.changing_row_color,
|
||||
row2.set_color, self.reference_row_color,
|
||||
)
|
||||
row1.target.next_to(self.rows, UP, buff=2)
|
||||
row1.target.align_to(row1, LEFT)
|
||||
|
||||
row2.target.next_to(row1.target, DOWN, buff=MED_LARGE_BUFF)
|
||||
lp, rp = row2_parens = TexMobject("()")
|
||||
row2_parens.set_height(row2.get_height() + 2 * SMALL_BUFF)
|
||||
lp.next_to(row2, LEFT, SMALL_BUFF)
|
||||
rp.next_to(row2, RIGHT, SMALL_BUFF)
|
||||
scalar = FractionMobject(scale_factor)
|
||||
scalar.next_to(lp, LEFT, SMALL_BUFF)
|
||||
scalar.add_plus_if_needed()
|
||||
|
||||
self.play(
|
||||
FadeIn(row2_parens),
|
||||
Write(scalar),
|
||||
)
|
||||
self.play(ReplacementTransform(row2.copy(), scaled_row2))
|
||||
self.wait()
|
||||
for elem, new_elem, s_elem in zip(row1, new_row1, scaled_row2):
|
||||
self.play(
|
||||
FadeOut(elem),
|
||||
FadeIn(new_elem),
|
||||
Transform(s_elem, new_elem.copy().fade(1), remover=True)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(label),
|
||||
FadeOut(row2_parens),
|
||||
FadeOut(scalar),
|
||||
new_row1.set_color, WHITE,
|
||||
row2.set_color, WHITE,
|
||||
)
|
||||
self.rows.submobjects[row1_index] = new_row1
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,32 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
class Birthday(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
sidelength = 6.0
|
||||
corner = np.array([-sidelength/2,-sidelength/2,0])
|
||||
nb_days_left = 365.0
|
||||
toggle = False
|
||||
|
||||
def probability():
|
||||
width = rect.get_width()
|
||||
height = rect.get_height()
|
||||
return width * height / sidelength**2
|
||||
|
||||
rect = Square().scale(sidelength/2)
|
||||
|
||||
while probability() > 0.5:
|
||||
|
||||
self.add(rect.copy())
|
||||
nb_days_left -= 1
|
||||
|
||||
if toggle:
|
||||
dim = 0
|
||||
else:
|
||||
dim = 1
|
||||
|
||||
rect.stretch_about_point(nb_days_left / 365, dim, corner)
|
||||
|
||||
toggle = not toggle
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
class Introduction(TeacherStudentsScene):
|
||||
|
||||
CONFIG = {
|
||||
"default_pi_creature_kwargs": {
|
||||
"color": MAROON_E,
|
||||
"flip_at_start": True,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.show_series()
|
||||
self.show_examples()
|
||||
|
||||
def show_series(self):
|
||||
series = VideoSeries(num_videos = 11)
|
||||
series.to_edge(UP)
|
||||
this_video = series[0]
|
||||
this_video.set_color(YELLOW)
|
||||
this_video.save_state()
|
||||
this_video.set_fill(opacity = 0)
|
||||
this_video.center()
|
||||
this_video.set_height(FRAME_HEIGHT)
|
||||
self.this_video = this_video
|
||||
|
||||
|
||||
words = TextMobject(
|
||||
"Welcome to \\\\",
|
||||
"Essence of Probability"
|
||||
)
|
||||
words.set_color_by_tex("Essence of Probability", YELLOW)
|
||||
|
||||
self.teacher.change_mode("happy")
|
||||
self.play(
|
||||
FadeIn(
|
||||
series,
|
||||
lag_ratio = 0.5,
|
||||
run_time = 2
|
||||
),
|
||||
Blink(self.get_teacher())
|
||||
)
|
||||
self.teacher_says(words, target_mode = "hooray")
|
||||
self.change_student_modes(
|
||||
*["hooray"]*3,
|
||||
look_at_arg = series[1].get_left(),
|
||||
added_anims = [
|
||||
ApplyMethod(this_video.restore, run_time = 3),
|
||||
]
|
||||
)
|
||||
self.play(*[
|
||||
ApplyMethod(
|
||||
video.shift, 0.5*video.get_height()*DOWN,
|
||||
run_time = 3,
|
||||
rate_func = squish_rate_func(
|
||||
there_and_back, alpha, alpha+0.3
|
||||
)
|
||||
)
|
||||
for video, alpha in zip(series, np.linspace(0, 0.7, len(series)))
|
||||
]+[
|
||||
Animation(self.teacher.bubble),
|
||||
Animation(self.teacher.bubble.content),
|
||||
])
|
||||
|
||||
self.play(
|
||||
FadeOut(self.teacher.bubble),
|
||||
FadeOut(self.teacher.bubble.content),
|
||||
self.get_teacher().change_mode, "raise_right_hand",
|
||||
*[
|
||||
ApplyMethod(pi.change_mode, "pondering")
|
||||
for pi in self.get_students()
|
||||
]
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.series = series
|
||||
|
||||
|
||||
def show_examples(self):
|
||||
|
||||
self.wait(10)
|
||||
# put examples here in video editor
|
||||
@@ -1,82 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
class Introduction(TeacherStudentsScene):
|
||||
|
||||
CONFIG = {
|
||||
"default_pi_creature_kwargs": {
|
||||
"color": MAROON_E,
|
||||
"flip_at_start": True,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.show_series()
|
||||
self.show_examples()
|
||||
|
||||
def show_series(self):
|
||||
series = VideoSeries(num_videos = 11)
|
||||
series.to_edge(UP)
|
||||
this_video = series[0]
|
||||
this_video.set_color(YELLOW)
|
||||
this_video.save_state()
|
||||
this_video.set_fill(opacity = 0)
|
||||
this_video.center()
|
||||
this_video.set_height(FRAME_HEIGHT)
|
||||
self.this_video = this_video
|
||||
|
||||
|
||||
words = TextMobject(
|
||||
"Welcome to \\\\",
|
||||
"Essence of Probability"
|
||||
)
|
||||
words.set_color_by_tex("Essence of Probability", YELLOW)
|
||||
|
||||
self.teacher.change_mode("happy")
|
||||
self.play(
|
||||
FadeIn(
|
||||
series,
|
||||
lag_ratio = 0.5,
|
||||
run_time = 2
|
||||
),
|
||||
Blink(self.get_teacher())
|
||||
)
|
||||
self.teacher_says(words, target_mode = "hooray")
|
||||
self.change_student_modes(
|
||||
*["hooray"]*3,
|
||||
look_at_arg = series[1].get_left(),
|
||||
added_anims = [
|
||||
ApplyMethod(this_video.restore, run_time = 3),
|
||||
]
|
||||
)
|
||||
self.play(*[
|
||||
ApplyMethod(
|
||||
video.shift, 0.5*video.get_height()*DOWN,
|
||||
run_time = 3,
|
||||
rate_func = squish_rate_func(
|
||||
there_and_back, alpha, alpha+0.3
|
||||
)
|
||||
)
|
||||
for video, alpha in zip(series, np.linspace(0, 0.7, len(series)))
|
||||
]+[
|
||||
Animation(self.teacher.bubble),
|
||||
Animation(self.teacher.bubble.content),
|
||||
])
|
||||
|
||||
self.play(
|
||||
FadeOut(self.teacher.bubble),
|
||||
FadeOut(self.teacher.bubble.content),
|
||||
self.get_teacher().change_mode, "raise_right_hand",
|
||||
*[
|
||||
ApplyMethod(pi.change_mode, "pondering")
|
||||
for pi in self.get_students()
|
||||
]
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.series = series
|
||||
|
||||
|
||||
def show_examples(self):
|
||||
|
||||
self.wait(10)
|
||||
# put examples here in video editor
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
class ShuffleThroughAllSequences(Scene):
|
||||
CONFIG = {
|
||||
"nb_coins" : 20,
|
||||
"run_time" : 5,
|
||||
"fps" : int(1.0/PRODUCTION_QUALITY_FRAME_DURATION),
|
||||
"coin_size" : 0.5,
|
||||
"coin_spacing" : 0.65
|
||||
}
|
||||
|
||||
|
||||
def construct(self):
|
||||
|
||||
nb_frames = self.run_time * self.fps
|
||||
nb_relevant_coins = int(np.log2(nb_frames)) + 1
|
||||
print("relevant coins:", nb_relevant_coins)
|
||||
nb_idle_coins = self.nb_coins - nb_relevant_coins
|
||||
|
||||
idle_heads = CoinSequence(nb_idle_coins * ["H"],
|
||||
radius = self.coin_size * 0.5,
|
||||
spacing = self.coin_spacing)
|
||||
idle_tails = CoinSequence(nb_idle_coins * ["T"],
|
||||
radius = self.coin_size * 0.5,
|
||||
spacing = self.coin_spacing)
|
||||
idle_tails.fade(0.5)
|
||||
|
||||
idle_part = VGroup(idle_heads, idle_tails)
|
||||
left_idle_part = CoinSequence(6 * ["H"],
|
||||
radius = self.coin_size * 0.5,
|
||||
spacing = self.coin_spacing)
|
||||
|
||||
#self.add(idle_part, left_idle_part)
|
||||
self.add(left_idle_part)
|
||||
last_coin_seq = VGroup()
|
||||
|
||||
for i in range(2**nb_relevant_coins):
|
||||
binary_seq = binary(i)
|
||||
# pad to the left with 0s
|
||||
nb_leading_zeroes = nb_relevant_coins - len(binary_seq)
|
||||
for j in range(nb_leading_zeroes):
|
||||
binary_seq.insert(0, 0)
|
||||
seq2 = ["H" if x == 0 else "T" for x in binary_seq]
|
||||
coin_seq = CoinSequence(seq2,
|
||||
radius = self.coin_size * 0.5,
|
||||
spacing = self.coin_spacing)
|
||||
coin_seq.next_to(idle_part, LEFT, buff = self.coin_spacing - self.coin_size)
|
||||
left_idle_part.next_to(coin_seq, LEFT, buff = self.coin_spacing - self.coin_size)
|
||||
all_coins = VGroup(left_idle_part, coin_seq) #, idle_part)
|
||||
all_coins.center()
|
||||
self.remove(last_coin_seq)
|
||||
self.add(coin_seq)
|
||||
#self.wait(1.0/self.fps)
|
||||
self.update_frame()
|
||||
self.add_frames(self.get_frame())
|
||||
last_coin_seq = coin_seq
|
||||
print(float(i)/2**nb_relevant_coins)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class IllustrateAreaModelBayes(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
color_A = YELLOW
|
||||
color_not_A = YELLOW_E
|
||||
color_B = MAROON
|
||||
color_not_B = MAROON_E
|
||||
opacity_B = 0.7
|
||||
|
||||
|
||||
# show independent events
|
||||
|
||||
sample_space_width = sample_space_height = 3
|
||||
p_of_A = 0.7
|
||||
p_of_not_A = 1 - p_of_A
|
||||
p_of_B = 0.8
|
||||
p_of_not_B = 1 - p_of_B
|
||||
|
||||
|
||||
rect_A = Rectangle(
|
||||
width = p_of_A * sample_space_width,
|
||||
height = 1 * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_A,
|
||||
fill_opacity = 1.0
|
||||
).move_to(3 * RIGHT + 1.5 * UP)
|
||||
|
||||
rect_not_A = Rectangle(
|
||||
width = p_of_not_A * sample_space_width,
|
||||
height = 1 * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_not_A,
|
||||
fill_opacity = 1.0
|
||||
).next_to(rect_A, RIGHT, buff = 0)
|
||||
|
||||
brace_A = Brace(rect_A, DOWN)
|
||||
label_A = TexMobject("P(A)").next_to(brace_A, DOWN).scale(0.7)
|
||||
brace_not_A = Brace(rect_not_A, DOWN)
|
||||
label_not_A = TexMobject("P(\\text{not }A)").next_to(brace_not_A, DOWN).scale(0.7)
|
||||
|
||||
# self.play(
|
||||
# LaggedStartMap(FadeIn, VGroup(rect_A, rect_not_A))
|
||||
# )
|
||||
# self.play(
|
||||
# ShowCreation(brace_A),
|
||||
# Write(label_A),
|
||||
# )
|
||||
|
||||
|
||||
|
||||
rect_B = Rectangle(
|
||||
width = 1 * sample_space_width,
|
||||
height = p_of_B * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_B,
|
||||
fill_opacity = opacity_B
|
||||
)
|
||||
rect_not_B = Rectangle(
|
||||
width = 1 * sample_space_width,
|
||||
height = p_of_not_B * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_not_B,
|
||||
fill_opacity = opacity_B
|
||||
).next_to(rect_B, UP, buff = 0)
|
||||
|
||||
VGroup(rect_B, rect_not_B).move_to(VGroup(rect_A, rect_not_A))
|
||||
|
||||
brace_B = Brace(rect_B, LEFT)
|
||||
label_B = TexMobject("P(B)").next_to(brace_B, LEFT).scale(0.7)
|
||||
brace_not_B = Brace(rect_not_B, LEFT)
|
||||
label_not_B = TexMobject("P(\\text{not }B)").next_to(brace_not_B, LEFT).scale(0.7)
|
||||
|
||||
# self.play(
|
||||
# LaggedStartMap(FadeIn, VGroup(rect_B, rect_not_B))
|
||||
# )
|
||||
# self.play(
|
||||
# ShowCreation(brace_B),
|
||||
# Write(label_B),
|
||||
# )
|
||||
|
||||
rect_A_and_B = Rectangle(
|
||||
width = p_of_A * sample_space_width,
|
||||
height = p_of_B * sample_space_height,
|
||||
stroke_width = 3,
|
||||
fill_opacity = 0.0
|
||||
).align_to(rect_A, DOWN).align_to(rect_A,LEFT)
|
||||
label_A_and_B = TexMobject("P(A\\text{ and }B)").scale(0.7)
|
||||
label_A_and_B.move_to(rect_A_and_B)
|
||||
|
||||
# self.play(
|
||||
# ShowCreation(rect_A_and_B)
|
||||
# )
|
||||
|
||||
indep_formula = TexMobject("P(A\\text{ and }B)", "=", "P(A)", "\cdot", "P(B)")
|
||||
indep_formula = indep_formula.scale(0.7)
|
||||
label_p_of_b = indep_formula.get_part_by_tex("P(B)")
|
||||
|
||||
label_A_and_B_copy = label_A_and_B.copy()
|
||||
label_A_copy = label_A.copy()
|
||||
label_B_copy = label_B.copy()
|
||||
# self.add(label_A_and_B_copy, label_A_copy, label_B_copy)
|
||||
|
||||
# self.play(Transform(label_A_and_B_copy, indep_formula[0]))
|
||||
# self.play(FadeIn(indep_formula[1]))
|
||||
# self.play(Transform(label_A_copy, indep_formula[2]))
|
||||
# self.play(FadeIn(indep_formula[3]))
|
||||
# self.play(Transform(label_B_copy, indep_formula[4]))
|
||||
|
||||
#self.wait()
|
||||
|
||||
label_A_and_B_copy = indep_formula[0]
|
||||
label_A_copy = indep_formula[2]
|
||||
label_B_copy = indep_formula[4]
|
||||
|
||||
# show conditional prob
|
||||
|
||||
rect_A_and_B.set_fill(color = RED, opacity = 0.5)
|
||||
rect_A_and_not_B = Rectangle(
|
||||
width = p_of_A * sample_space_width,
|
||||
height = p_of_not_B * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_not_B,
|
||||
fill_opacity = opacity_B
|
||||
).next_to(rect_A_and_B, UP, buff = 0)
|
||||
|
||||
rect_not_A_and_B = Rectangle(
|
||||
width = p_of_not_A * sample_space_width,
|
||||
height = p_of_B * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_B,
|
||||
fill_opacity = opacity_B
|
||||
).next_to(rect_A_and_B, RIGHT, buff = 0)
|
||||
|
||||
rect_not_A_and_not_B = Rectangle(
|
||||
width = p_of_not_A * sample_space_width,
|
||||
height = p_of_not_B * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_not_B,
|
||||
fill_opacity = opacity_B
|
||||
).next_to(rect_not_A_and_B, UP, buff = 0)
|
||||
|
||||
|
||||
indep_formula.next_to(rect_not_A, LEFT, buff = 5)
|
||||
#indep_formula.shift(UP)
|
||||
|
||||
self.play(Write(indep_formula))
|
||||
|
||||
self.play(
|
||||
FadeIn(VGroup(
|
||||
rect_A, rect_not_A, brace_A, label_A, brace_B, label_B,
|
||||
rect_A_and_not_B, rect_not_A_and_B, rect_not_A_and_not_B,
|
||||
rect_A_and_B,
|
||||
label_A_and_B,
|
||||
))
|
||||
)
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
p_of_B_knowing_A = 0.6
|
||||
rect_A_and_B.target = Rectangle(
|
||||
width = p_of_A * sample_space_width,
|
||||
height = p_of_B_knowing_A * sample_space_height,
|
||||
stroke_width = 3,
|
||||
fill_color = color_B,
|
||||
fill_opacity = opacity_B
|
||||
).align_to(rect_A_and_B, DOWN).align_to(rect_A_and_B, LEFT)
|
||||
|
||||
rect_A_and_not_B.target = Rectangle(
|
||||
width = p_of_A * sample_space_width,
|
||||
height = (1 - p_of_B_knowing_A) * sample_space_height,
|
||||
stroke_width = 0,
|
||||
fill_color = color_not_B,
|
||||
fill_opacity = opacity_B
|
||||
).next_to(rect_A_and_B.target, UP, buff = 0)
|
||||
|
||||
brace_B.target = Brace(rect_A_and_B.target, LEFT)
|
||||
label_B.target = TexMobject("P(B\mid A)").scale(0.7).next_to(brace_B.target, LEFT)
|
||||
|
||||
|
||||
self.play(
|
||||
MoveToTarget(rect_A_and_B),
|
||||
MoveToTarget(rect_A_and_not_B),
|
||||
MoveToTarget(brace_B),
|
||||
MoveToTarget(label_B),
|
||||
label_A_and_B.move_to,rect_A_and_B.target
|
||||
)
|
||||
label_B_knowing_A = label_B
|
||||
|
||||
#self.play(FadeOut(label_B_copy))
|
||||
self.remove(indep_formula.get_part_by_tex("P(B)"))
|
||||
indep_formula.remove(indep_formula.get_part_by_tex("P(B)"))
|
||||
label_B_knowing_A_copy = label_B_knowing_A.copy()
|
||||
self.add(label_B_knowing_A_copy)
|
||||
|
||||
self.play(
|
||||
label_B_knowing_A_copy.next_to, indep_formula.get_part_by_tex("\cdot"), RIGHT,
|
||||
)
|
||||
|
||||
# solve formula for P(B|A)
|
||||
|
||||
rearranged_formula = TexMobject("P(B\mid A)", "=", "{P(A\\text{ and }B) \over P(A)}")
|
||||
rearranged_formula.move_to(indep_formula)
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
self.play(
|
||||
# in some places get_part_by_tex does not find the correct part
|
||||
# so I picked out fitting indices
|
||||
label_B_knowing_A_copy.move_to, rearranged_formula.get_part_by_tex("P(B\mid A)"),
|
||||
label_A_copy.move_to, rearranged_formula[-1][10],
|
||||
label_A_and_B_copy.move_to, rearranged_formula[-1][3],
|
||||
indep_formula.get_part_by_tex("=").move_to, rearranged_formula.get_part_by_tex("="),
|
||||
Transform(indep_formula.get_part_by_tex("\cdot"), rearranged_formula[2][8]),
|
||||
)
|
||||
|
||||
rect = SurroundingRectangle(rearranged_formula, buff = 0.5 * MED_LARGE_BUFF)
|
||||
self.play(ShowCreation(rect))
|
||||
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from old_projects.eoc.chapter8 import *
|
||||
|
||||
import scipy.special
|
||||
|
||||
class IllustrateAreaModelErf(GraphScene):
|
||||
CONFIG = {
|
||||
"x_min" : -3.0,
|
||||
"x_max" : 3.0,
|
||||
"y_min" : 0,
|
||||
"y_max" : 1.0,
|
||||
"num_rects": 400,
|
||||
"y_axis_label" : "",
|
||||
"x_axis_label" : "",
|
||||
"variable_point_label" : "a",
|
||||
"graph_origin": 2.5 * DOWN + 4 * RIGHT,
|
||||
"x_axis_width": 5,
|
||||
"y_axis_height": 5
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
# integral bounds
|
||||
x_min_1 = -0.0001
|
||||
x_max_1 = 0.0001
|
||||
|
||||
x_min_2 = self.x_min
|
||||
x_max_2 = self.x_max
|
||||
|
||||
self.setup_axes()
|
||||
self.remove(self.x_axis, self.y_axis)
|
||||
graph = self.get_graph(lambda x: np.exp(-x**2) * 2.0 / TAU ** 0.5)
|
||||
area = self.area = self.get_area(graph, x_min_1, x_max_1)
|
||||
|
||||
|
||||
pdf_formula = TexMobject("p(x) = {1\over \sigma\sqrt{2\pi}}e^{-{1\over 2}({x\over\sigma})^2}")
|
||||
pdf_formula.set_color(graph.color)
|
||||
|
||||
cdf_formula = TexMobject("P(|X| < ", "a", ") = \int", "_{-a}", "^a", "p(x) dx")
|
||||
cdf_formula.set_color_by_tex("a", YELLOW)
|
||||
cdf_formula.next_to(graph, LEFT, buff = 2)
|
||||
pdf_formula.next_to(cdf_formula, UP)
|
||||
|
||||
formulas = VGroup(pdf_formula, cdf_formula)
|
||||
self.play(Write(pdf_formula))
|
||||
self.play(Write(cdf_formula))
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
self.play(ShowCreation(self.x_axis))
|
||||
self.play(ShowCreation(graph))
|
||||
self.play(FadeIn(area))
|
||||
|
||||
self.v_graph = graph
|
||||
self.add_T_label(
|
||||
x_min_1,
|
||||
label = "-a",
|
||||
side = LEFT,
|
||||
color = YELLOW,
|
||||
animated = False
|
||||
)
|
||||
self.add_T_label(
|
||||
x_max_1,
|
||||
label = "a",
|
||||
side = RIGHT,
|
||||
color = YELLOW,
|
||||
animated = False
|
||||
)
|
||||
# don't show the labels just yet
|
||||
self.remove(
|
||||
self.left_T_label_group[0],
|
||||
self.right_T_label_group[0],
|
||||
)
|
||||
|
||||
def integral_update_func(t):
|
||||
return scipy.special.erf(
|
||||
self.point_to_coords(self.right_v_line.get_center())[0]
|
||||
)
|
||||
|
||||
def integral_update_func_percent(t):
|
||||
return 100 * integral_update_func(t)
|
||||
|
||||
equals_sign = TexMobject("=").next_to(cdf_formula, buff = MED_LARGE_BUFF)
|
||||
|
||||
cdf_value = DecimalNumber(0, color = graph.color, num_decimal_places = 3)
|
||||
cdf_value.next_to(equals_sign)
|
||||
self.play(
|
||||
FadeIn(equals_sign),
|
||||
FadeIn(cdf_value)
|
||||
)
|
||||
self.add_foreground_mobject(cdf_value)
|
||||
|
||||
cdf_percentage = DecimalNumber(0, unit = "\\%")
|
||||
cdf_percentage.move_to(self.coords_to_point(0,0.2))
|
||||
self.add_foreground_mobject(cdf_percentage)
|
||||
|
||||
cdf_value.add_updater(
|
||||
lambda m: m.set_value(integral_update_func())
|
||||
)
|
||||
|
||||
anim = self.get_animation_integral_bounds_change(
|
||||
graph, x_min_2, x_max_2,
|
||||
run_time = 3)
|
||||
|
||||
|
||||
self.play(
|
||||
anim
|
||||
)
|
||||
|
||||
rect = SurroundingRectangle(formulas, buff = 0.5 * MED_LARGE_BUFF)
|
||||
self.play(ShowCreation(rect))
|
||||
@@ -1,93 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
|
||||
class IllustrateAreaModelExpectation(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
formula = TexMobject("E[X] = \sum_{i=1}^N p_i x_i").move_to(3 * LEFT + UP)
|
||||
self.play(Write(formula))
|
||||
|
||||
|
||||
x_scale = 5.0
|
||||
y_scale = 1.0
|
||||
|
||||
probabilities = np.array([1./8, 3./8, 3./8, 1./8])
|
||||
prob_strings = ["{1\over 8}","{3\over 8}","{3\over 8}","{1\over 8}"]
|
||||
cumulative_probabilities = np.cumsum(probabilities)
|
||||
cumulative_probabilities = np.insert(cumulative_probabilities, 0, 0)
|
||||
y_values = np.array([0, 1, 2, 3])
|
||||
|
||||
hist = Histogram(probabilities, y_values,
|
||||
mode = "widths",
|
||||
x_scale = x_scale,
|
||||
y_scale = y_scale,
|
||||
x_labels = "none"
|
||||
)
|
||||
|
||||
flat_hist = Histogram(probabilities, 0 * y_values,
|
||||
mode = "widths",
|
||||
x_scale = x_scale,
|
||||
y_scale = y_scale,
|
||||
x_labels = "none"
|
||||
)
|
||||
|
||||
self.play(FadeIn(flat_hist))
|
||||
self.play(
|
||||
ReplacementTransform(flat_hist, hist)
|
||||
)
|
||||
|
||||
braces = VGroup()
|
||||
p_labels = VGroup()
|
||||
# add x labels (braces)
|
||||
for (p,string,bar) in zip(probabilities, prob_strings,hist.bars):
|
||||
brace = Brace(bar, DOWN, buff = 0.1)
|
||||
p_label = TexMobject(string).next_to(brace, DOWN, buff = SMALL_BUFF).scale(0.7)
|
||||
group = VGroup(brace, p_label)
|
||||
braces.add(brace)
|
||||
p_labels.add(p_label)
|
||||
|
||||
self.play(
|
||||
LaggedStartMap(FadeIn,braces),
|
||||
LaggedStartMap(FadeIn, p_labels)
|
||||
)
|
||||
|
||||
|
||||
|
||||
y_average = np.mean(y_values)
|
||||
averaged_y_values = y_average * np.ones(np.shape(y_values))
|
||||
|
||||
averaged_hist = flat_hist = Histogram(probabilities, averaged_y_values,
|
||||
mode = "widths",
|
||||
x_scale = x_scale,
|
||||
y_scale = y_scale,
|
||||
x_labels = "none",
|
||||
y_labels = "none"
|
||||
).fade(0.2)
|
||||
|
||||
ghost_hist = hist.copy().fade(0.8)
|
||||
self.bring_to_back(ghost_hist)
|
||||
|
||||
self.play(Transform(hist, averaged_hist, run_time = 3))
|
||||
self.wait()
|
||||
|
||||
average_brace = Brace(averaged_hist, RIGHT, buff = 0.1)
|
||||
average_label = TexMobject(str(y_average)).scale(0.7)
|
||||
average_label.next_to(average_brace, RIGHT, SMALL_BUFF)
|
||||
average_group = VGroup(average_brace, average_label)
|
||||
|
||||
one_brace = Brace(averaged_hist, DOWN, buff = 0.1)
|
||||
one_p_label = TexMobject(str(1)).next_to(one_brace, DOWN, buff = SMALL_BUFF).scale(0.7)
|
||||
one_group = VGroup(one_brace, one_p_label)
|
||||
|
||||
self.play(
|
||||
FadeIn(average_group),
|
||||
Transform(braces, one_brace),
|
||||
Transform(p_labels, one_p_label),
|
||||
)
|
||||
|
||||
rect = SurroundingRectangle(formula, buff = 0.5 * MED_LARGE_BUFF)
|
||||
self.play(ShowCreation(rect))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,164 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
from active_projects.eop.chapter1.brick_row_scene import BrickRowScene
|
||||
|
||||
class EntireBrickWall(BrickRowScene, MovingCameraScene):
|
||||
|
||||
def setup(self):
|
||||
super(BrickRowScene, self).setup()
|
||||
super(PiCreatureScene, self).setup()
|
||||
|
||||
|
||||
def construct(self):
|
||||
|
||||
self.remove(self.get_primary_pi_creature())
|
||||
|
||||
row_height = 0.3
|
||||
nb_rows = 20
|
||||
start_point = 3 * UP + 1 * LEFT
|
||||
|
||||
rows = VMobject()
|
||||
rows.add(BrickRow(0, height = row_height))
|
||||
rows.move_to(start_point)
|
||||
self.add(rows)
|
||||
|
||||
zero_counter = Integer(0).next_to(start_point + 0.5 * rows[0].width * RIGHT)
|
||||
nb_flips_text = TextMobject("\# of flips")
|
||||
nb_flips_text.next_to(zero_counter, RIGHT, buff = LARGE_BUFF)
|
||||
self.add(zero_counter, nb_flips_text)
|
||||
flip_counters = VGroup(zero_counter)
|
||||
|
||||
for i in range(1, nb_rows + 1):
|
||||
rows.add(rows[-1].copy())
|
||||
self.bring_to_back(rows[-1])
|
||||
anims = [
|
||||
rows[-1].shift, row_height * DOWN,
|
||||
Animation(rows[-2])
|
||||
]
|
||||
|
||||
if i % 5 == 0:
|
||||
counter = Integer(i)
|
||||
counter.next_to(rows[-1].get_right() + row_height * DOWN, RIGHT)
|
||||
flip_counters.add(counter)
|
||||
anims.append(FadeIn(counter))
|
||||
|
||||
self.play(*anims)
|
||||
|
||||
self.play(SplitRectsInBrickWall(rows[-1]))
|
||||
rows.submobjects[-1] = self.merge_rects_by_subdiv(rows[-1])
|
||||
rows.submobjects[-1] = self.merge_rects_by_coloring(rows[-1])
|
||||
|
||||
|
||||
# draw indices under the last row for the number of tails
|
||||
tails_counters = VGroup()
|
||||
for (i, rect) in enumerate(rows[-1].rects):
|
||||
if i < 6 or i > 14:
|
||||
continue
|
||||
if i == 6:
|
||||
counter = TexMobject("\dots", color = COLOR_TAILS)
|
||||
counter.next_to(rect, DOWN, buff = 1.5 * MED_SMALL_BUFF)
|
||||
elif i == 14:
|
||||
counter = TexMobject("\dots", color = COLOR_TAILS)
|
||||
counter.next_to(rect, DOWN, buff = 1.5 * MED_SMALL_BUFF)
|
||||
counter.shift(0.2 * RIGHT)
|
||||
else:
|
||||
counter = Integer(i, color = COLOR_TAILS)
|
||||
counter.next_to(rect, DOWN)
|
||||
tails_counters.add(counter)
|
||||
|
||||
nb_tails_text = TextMobject("\# of tails", color = COLOR_TAILS)
|
||||
nb_tails_text.next_to(tails_counters[-1], RIGHT, buff = LARGE_BUFF)
|
||||
|
||||
self.play(
|
||||
LaggedStartMap(FadeIn, tails_counters),
|
||||
FadeIn(nb_tails_text)
|
||||
)
|
||||
|
||||
# remove any hidden brick rows
|
||||
self.clear()
|
||||
self.add(nb_flips_text)
|
||||
|
||||
mobs_to_shift = VGroup(
|
||||
rows, flip_counters, tails_counters, nb_tails_text,
|
||||
)
|
||||
self.play(mobs_to_shift.shift, 3 * UP)
|
||||
|
||||
last_row_rect = SurroundingRectangle(rows[-1], buff = 0)
|
||||
last_row_rect.set_stroke(color = YELLOW, width = 6)
|
||||
|
||||
rows.save_state()
|
||||
self.play(
|
||||
rows.fade, 0.9,
|
||||
ShowCreation(last_row_rect)
|
||||
)
|
||||
|
||||
def highlighted_brick(row = 20, nb_tails = 10):
|
||||
brick_copy = rows[row].rects[nb_tails].copy()
|
||||
brick_copy.set_fill(color = YELLOW, opacity = 0.8)
|
||||
prob_percentage = float(choose(row, nb_tails)) / 2**row * 100
|
||||
brick_label = DecimalNumber(prob_percentage,
|
||||
unit = "\%", num_decimal_places = 1, color = BLACK)
|
||||
brick_label.move_to(brick_copy)
|
||||
brick_label.set_height(0.8 * brick_copy.get_height())
|
||||
return VGroup(brick_copy, brick_label)
|
||||
|
||||
highlighted_bricks = [
|
||||
highlighted_brick(row = 20, nb_tails = i)
|
||||
for i in range(20)
|
||||
]
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeIn(highlighted_bricks[10])
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(highlighted_bricks[10]),
|
||||
FadeIn(highlighted_bricks[9]),
|
||||
FadeIn(highlighted_bricks[11]),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(highlighted_bricks[9]),
|
||||
FadeOut(highlighted_bricks[11]),
|
||||
FadeIn(highlighted_bricks[8]),
|
||||
FadeIn(highlighted_bricks[12]),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(highlighted_bricks[8]),
|
||||
FadeOut(highlighted_bricks[12]),
|
||||
FadeOut(last_row_rect),
|
||||
rows.restore,
|
||||
)
|
||||
self.wait()
|
||||
new_frame = self.camera_frame.copy()
|
||||
new_frame.scale(0.0001).move_to(rows.get_corner(DR))
|
||||
|
||||
self.play(
|
||||
Transform(self.camera_frame, new_frame,
|
||||
run_time = 9,
|
||||
rate_func = exponential_decay
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
class Chapter1OpeningQuote(OpeningQuote):
|
||||
CONFIG = {
|
||||
"fade_in_kwargs": {
|
||||
"lag_ratio": 0.5,
|
||||
"rate_func": linear,
|
||||
"run_time": 10,
|
||||
},
|
||||
"text_size" : "\\normalsize",
|
||||
"use_quotation_marks": False,
|
||||
"quote" : [
|
||||
"To see a world in a grain of sand\\\\",
|
||||
"And a heaven in a wild flower,\\\\",
|
||||
"Hold infinity in the palm of your hand\\\\",
|
||||
"\phantom{r}And eternity in an hour.\\\\"
|
||||
],
|
||||
"quote_arg_separator" : " ",
|
||||
"highlighted_quote_terms" : {},
|
||||
"author" : "William Blake: \\\\ \emph{Auguries of Innocence}",
|
||||
}
|
||||
|
||||
class Introduction(TeacherStudentsScene):
|
||||
|
||||
CONFIG = {
|
||||
"default_pi_creature_kwargs": {
|
||||
"color": MAROON_E,
|
||||
"flip_at_start": True,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
self.wait(5)
|
||||
|
||||
self.change_student_modes(
|
||||
"confused", "frustrated", "dejected",
|
||||
look_at_arg = UP + 2 * RIGHT
|
||||
)
|
||||
|
||||
self.wait()
|
||||
|
||||
self.play(
|
||||
self.get_teacher().change_mode,"raise_right_hand"
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.wait(30)
|
||||
# put examples here in video editor
|
||||
|
||||
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# show examples of the area model #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
class JustFlipping(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
randy = CoinFlippingPiCreature(color = MAROON_E, flip_height = 1).shift(2 * DOWN)
|
||||
self.add(randy)
|
||||
|
||||
self.wait(2)
|
||||
|
||||
for i in range(10):
|
||||
self.wait()
|
||||
self.play(FlipCoin(randy))
|
||||
|
||||
|
||||
|
||||
class JustFlippingWithResults(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
randy = CoinFlippingPiCreature(color = MAROON_E, flip_height = 1).shift(2 * DOWN)
|
||||
self.add(randy)
|
||||
|
||||
self.wait(2)
|
||||
|
||||
for i in range(10):
|
||||
self.wait()
|
||||
self.play(FlipCoin(randy))
|
||||
result = random.choice(["H", "T"])
|
||||
if result == "H":
|
||||
coin = UprightHeads().scale(3)
|
||||
else:
|
||||
coin = UprightTails().scale(3)
|
||||
coin.move_to(2 * UP + 2.5 * LEFT + i * 0.6 * RIGHT)
|
||||
self.play(FadeIn(coin))
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
|
||||
class MillionFlips(Scene):
|
||||
def construct(self):
|
||||
title = TextMobject("1{,}000{,}000 flips")
|
||||
title.to_edge(UP)
|
||||
self.add(title)
|
||||
|
||||
small_wait_time = 1.0 / 15 # Um...
|
||||
|
||||
n_flips_label = TextMobject("\\# Flips: ")
|
||||
n_heads_label = TextMobject("\\# Heads: ")
|
||||
n_flips_count = Integer(0)
|
||||
n_heads_count = Integer(0)
|
||||
n_heads_label.to_edge(RIGHT, buff=2 * LARGE_BUFF)
|
||||
n_flips_label.next_to(n_heads_label, DOWN, aligned_edge=LEFT)
|
||||
n_flips_count.next_to(n_flips_label[-1], RIGHT)
|
||||
n_heads_count.next_to(n_heads_label[-1], RIGHT)
|
||||
VGroup(n_flips_count, n_heads_count).shift(0.5 * SMALL_BUFF * UP)
|
||||
self.add(n_flips_label, n_heads_label, n_flips_count, n_heads_count)
|
||||
|
||||
coins = VGroup(*[
|
||||
FlatHeads() if random.random() < 0.5 else FlatTails()
|
||||
for x in range(100)
|
||||
])
|
||||
self.organize_group(coins)
|
||||
|
||||
proportions = np.random.normal(0.5, 0.5 * 0.1, 100)
|
||||
hundred_boxes = VGroup(*[
|
||||
Square(
|
||||
stroke_width=1,
|
||||
stroke_color=WHITE,
|
||||
fill_opacity=1,
|
||||
fill_color=interpolate_color(COLOR_HEADS, COLOR_TAILS, prop)
|
||||
)
|
||||
for prop in proportions
|
||||
])
|
||||
self.organize_group(hundred_boxes)
|
||||
|
||||
ten_k_proportions = np.random.normal(0.5, 0.5 * 0.01, 100)
|
||||
ten_k_boxes = VGroup(*[
|
||||
Square(
|
||||
stroke_width=1,
|
||||
stroke_color=WHITE,
|
||||
fill_opacity=1,
|
||||
fill_color=interpolate_color(COLOR_HEADS, COLOR_TAILS, prop)
|
||||
)
|
||||
for prop in ten_k_proportions
|
||||
])
|
||||
self.organize_group(ten_k_boxes)
|
||||
|
||||
# Animations
|
||||
for coin in coins:
|
||||
self.add(coin)
|
||||
self.increment(n_flips_count)
|
||||
if isinstance(coin, FlatHeads):
|
||||
self.increment(n_heads_count)
|
||||
self.wait(small_wait_time)
|
||||
|
||||
self.play(
|
||||
FadeIn(hundred_boxes[0]),
|
||||
coins.set_stroke, {"width": 0},
|
||||
coins.replace, hundred_boxes[0]
|
||||
)
|
||||
hundred_boxes[0].add(coins)
|
||||
for box, prop in list(zip(hundred_boxes, proportions))[1:]:
|
||||
self.add(box)
|
||||
self.increment(n_flips_count, 100)
|
||||
self.increment(n_heads_count, int(np.round(prop * 100)))
|
||||
self.wait(small_wait_time)
|
||||
|
||||
self.play(
|
||||
FadeIn(ten_k_boxes[0]),
|
||||
hundred_boxes.set_stroke, {"width": 0},
|
||||
hundred_boxes.replace, ten_k_boxes[0]
|
||||
)
|
||||
ten_k_boxes[0].add(hundred_boxes)
|
||||
for box, prop in list(zip(ten_k_boxes, ten_k_proportions))[1:]:
|
||||
self.add(box)
|
||||
self.increment(n_flips_count, 10000)
|
||||
self.increment(n_heads_count, int(np.round(prop * 10000)))
|
||||
self.wait(small_wait_time)
|
||||
self.wait()
|
||||
|
||||
def organize_group(self, group):
|
||||
group.arrange_in_grid(10)
|
||||
group.set_height(5)
|
||||
group.shift(DOWN + 2 * LEFT)
|
||||
|
||||
def increment(self, integer_mob, value=1):
|
||||
new_int = Integer(integer_mob.number + value)
|
||||
new_int.move_to(integer_mob, DL)
|
||||
integer_mob.number += value
|
||||
integer_mob.submobjects = new_int.submobjects
|
||||
|
||||
|
||||
class PropHeadsWithinThousandth(Scene):
|
||||
def construct(self):
|
||||
prob = TexMobject(
|
||||
"P(499{,}000 \\le", "\\# \\text{H}", "\\le 501{,}000)",
|
||||
"\\approx", "0.9545",
|
||||
)
|
||||
prob[1].set_color(RED)
|
||||
prob[-1].set_color(YELLOW)
|
||||
self.add(prob)
|
||||
|
||||
|
||||
class PropHeadsWithinHundredth(Scene):
|
||||
def construct(self):
|
||||
prob = TexMobject(
|
||||
"P(490{,}000 \\le", "\\# \\text{H}", "\\le 510{,}000)",
|
||||
"\\approx", "0.99999999\\dots",
|
||||
)
|
||||
prob[1].set_color(RED)
|
||||
prob[-1].set_color(YELLOW)
|
||||
self.add(prob)
|
||||
@@ -1,299 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
class GenericMorphBrickRowIntoHistogram(Scene):
|
||||
|
||||
CONFIG = {
|
||||
"level" : 3,
|
||||
"bar_width" : 2.0,
|
||||
"bar_anchor_height" : -3.0,
|
||||
"show_tallies" : False,
|
||||
"show_nb_flips" : True
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
self.row = BrickRow(self.level, height = self.bar_width, width = 10)
|
||||
self.bars = OutlineableBars(*[self.row.rects[i] for i in range(self.level + 1)])
|
||||
self.bar_anchors = [self.bar_anchor_height * UP + self.row.height * (i - 0.5 * self.level) * RIGHT for i in range(self.level + 1)]
|
||||
|
||||
self.add(self.row)
|
||||
|
||||
if self.show_tallies:
|
||||
|
||||
tallies = VMobject()
|
||||
|
||||
for (i,brick) in enumerate(self.row.rects):
|
||||
tally = TallyStack(self.level - i, i)
|
||||
tally.move_to(brick)
|
||||
self.add(tally)
|
||||
tallies.add(tally)
|
||||
brick.set_stroke(width = 3)
|
||||
|
||||
if self.show_nb_flips:
|
||||
nb_flips_text = TextMobject("\# of flips: " + str(self.level))
|
||||
nb_flips_text.to_corner(UR)
|
||||
self.add(nb_flips_text)
|
||||
|
||||
self.remove(self.row.subdivs, self.row.border)
|
||||
|
||||
for rect in self.row.rects:
|
||||
rect.set_stroke(color = WHITE, width = 3)
|
||||
|
||||
self.wait()
|
||||
self.play(
|
||||
self.row.rects.space_out_submobjects, {"factor" : 1.3},
|
||||
FadeOut(tallies)
|
||||
)
|
||||
self.wait()
|
||||
anims = []
|
||||
for brick in self.row.rects:
|
||||
anims.append(brick.rotate)
|
||||
anims.append(TAU/4)
|
||||
|
||||
self.play(*anims)
|
||||
|
||||
self.wait()
|
||||
|
||||
anims = []
|
||||
for (i,brick) in enumerate(self.row.rects):
|
||||
anims.append(brick.next_to)
|
||||
anims.append(self.bar_anchors[i])
|
||||
anims.append({"direction" : UP, "buff" : 0})
|
||||
self.play(*anims)
|
||||
self.wait()
|
||||
|
||||
self.bars.create_outline()
|
||||
anims = [
|
||||
ApplyMethod(rect.set_stroke, {"width" : 0})
|
||||
for rect in self.bars
|
||||
]
|
||||
anims.append(FadeIn(self.bars.outline))
|
||||
self.play(*anims)
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
|
||||
class MorphBrickRowIntoHistogram3(GenericMorphBrickRowIntoHistogram):
|
||||
|
||||
CONFIG = {
|
||||
"level" : 3,
|
||||
"prob_denominator" : 8,
|
||||
"bar_width" : 2.0,
|
||||
"bar_anchor_height" : -3.0,
|
||||
"show_tallies" : True,
|
||||
"show_nb_flips" : False
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
|
||||
|
||||
super(MorphBrickRowIntoHistogram3,self).construct()
|
||||
|
||||
|
||||
# draw x-axis
|
||||
|
||||
x_axis = Line(ORIGIN, 10 * RIGHT, color = WHITE, buff = 0)
|
||||
x_axis.next_to(self.bars, DOWN, buff = 0)
|
||||
#x_labels = VMobject(*[TexMobject(str(i)) for i in range(4)])
|
||||
x_labels = VMobject()
|
||||
|
||||
for (i, bar) in enumerate(self.bars):
|
||||
label = Integer(i)
|
||||
label.next_to(self.bar_anchors[i], DOWN)
|
||||
x_labels.add(label)
|
||||
|
||||
nb_tails_label = TextMobject("\# of tails")
|
||||
nb_tails_label.next_to(x_labels[-1], RIGHT, MED_LARGE_BUFF)
|
||||
|
||||
|
||||
|
||||
# draw y-guides
|
||||
|
||||
y_guides = VMobject()
|
||||
for i in range(0,self.prob_denominator + 1):
|
||||
y_guide = Line(5 * LEFT, 5 * RIGHT, stroke_color = GRAY)
|
||||
y_guide.move_to(self.bar_anchor_height * UP + i * float(self.row.width) / self.prob_denominator * UP)
|
||||
y_guide_label = TexMobject("{" + str(i) + "\over " + str(self.prob_denominator) + "}", color = GRAY)
|
||||
y_guide_label.scale(0.7)
|
||||
y_guide_label.next_to(y_guide, LEFT)
|
||||
if i != 0:
|
||||
y_guide.add(y_guide_label)
|
||||
y_guides.add(y_guide)
|
||||
self.wait()
|
||||
|
||||
self.play(
|
||||
FadeIn(y_guides),
|
||||
Animation(self.bars.outline),
|
||||
Animation(self.bars)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.play(
|
||||
FadeIn(x_axis),
|
||||
FadeIn(x_labels),
|
||||
FadeIn(nb_tails_label)
|
||||
)
|
||||
|
||||
self.add_foreground_mobject(nb_tails_label)
|
||||
area_color = YELLOW
|
||||
|
||||
total_area_text = TextMobject("total area =", color = area_color)
|
||||
area_decimal = DecimalNumber(0, color = area_color, num_decimal_places = 3)
|
||||
area_decimal.next_to(total_area_text, RIGHT)
|
||||
|
||||
total_area_group = VGroup(total_area_text, area_decimal)
|
||||
total_area_group.move_to(2.7 * UP)
|
||||
self.wait()
|
||||
|
||||
self.play(
|
||||
FadeIn(total_area_text),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
cumulative_areas = [0.125, 0.5, 0.875, 1]
|
||||
covering_rects = self.bars.copy()
|
||||
for (i,rect) in enumerate(covering_rects):
|
||||
rect.set_fill(color = area_color, opacity = 0.5)
|
||||
self.play(
|
||||
FadeIn(rect, rate_func = linear),
|
||||
ChangeDecimalToValue(area_decimal, cumulative_areas[i],
|
||||
rate_func = linear)
|
||||
)
|
||||
self.wait(0.2)
|
||||
|
||||
self.wait()
|
||||
|
||||
total_area_rect = SurroundingRectangle(
|
||||
total_area_group,
|
||||
buff = MED_SMALL_BUFF,
|
||||
stroke_color = area_color
|
||||
)
|
||||
|
||||
self.play(
|
||||
FadeOut(covering_rects),
|
||||
ShowCreation(total_area_rect)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
|
||||
class MorphBrickRowIntoHistogram20(GenericMorphBrickRowIntoHistogram):
|
||||
CONFIG = {
|
||||
"level" : 20,
|
||||
"prob_ticks" : 0.05,
|
||||
"bar_width" : 0.5,
|
||||
"bar_anchor_height" : -3.0,
|
||||
"x_ticks": 5
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
super(MorphBrickRowIntoHistogram20, self).construct()
|
||||
|
||||
x_axis = Line(ORIGIN, 10 * RIGHT, color = WHITE, buff = 0)
|
||||
x_axis.next_to(self.bars, DOWN, buff = 0)
|
||||
#x_labels = VMobject(*[TexMobject(str(i)) for i in range(4)])
|
||||
x_labels = VMobject()
|
||||
for (i, bar) in enumerate(self.bars):
|
||||
if i % self.x_ticks != 0:
|
||||
continue
|
||||
label = Integer(i)
|
||||
label.next_to(self.bar_anchors[i], DOWN)
|
||||
x_labels.add(label)
|
||||
|
||||
nb_tails_label = TextMobject("\# of tails")
|
||||
nb_tails_label.move_to(5 * RIGHT + 2.5 * DOWN)
|
||||
self.wait()
|
||||
|
||||
self.play(
|
||||
FadeIn(x_axis),
|
||||
FadeIn(x_labels),
|
||||
FadeIn(nb_tails_label)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# draw y-guides
|
||||
|
||||
max_prob = float(choose(self.level, self.level/2)) / 2 ** self.level
|
||||
|
||||
y_guides = VMobject()
|
||||
y_guide_heights = []
|
||||
prob_grid = np.arange(self.prob_ticks, 1.3 * max_prob, self.prob_ticks)
|
||||
for i in prob_grid:
|
||||
y_guide = Line(5 * LEFT, 5 * RIGHT, stroke_color = GRAY)
|
||||
y_guide_height = self.bar_anchor_height + i * float(self.row.width)
|
||||
y_guide_heights.append(y_guide_height)
|
||||
y_guide.move_to(y_guide_height * UP)
|
||||
y_guide_label = DecimalNumber(i, num_decimal_places = 2, color = GRAY)
|
||||
y_guide_label.scale(0.7)
|
||||
y_guide_label.next_to(y_guide, LEFT)
|
||||
y_guide.add(y_guide_label)
|
||||
y_guides.add(y_guide)
|
||||
|
||||
self.bring_to_back(y_guides)
|
||||
self.play(FadeIn(y_guides), Animation(self.bars))
|
||||
self.wait()
|
||||
|
||||
histogram_width = self.bars.get_width()
|
||||
histogram_height = self.bars.get_height()
|
||||
|
||||
# scale to fit screen
|
||||
self.scale_x = 10.0/((len(self.bars) - 1) * self.bar_width)
|
||||
self.scale_y = 6.0/histogram_height
|
||||
|
||||
|
||||
anims = []
|
||||
for (bar, x_label) in zip(self.bars, x_labels):
|
||||
v = (self.scale_x - 1) * x_label.get_center()[0] * RIGHT
|
||||
anims.append(x_label.shift)
|
||||
anims.append(v)
|
||||
|
||||
|
||||
anims.append(self.bars.stretch_about_point)
|
||||
anims.append(self.scale_x)
|
||||
anims.append(0)
|
||||
anims.append(ORIGIN)
|
||||
anims.append(self.bars.outline.stretch_about_point)
|
||||
anims.append(self.scale_x)
|
||||
anims.append(0)
|
||||
anims.append(ORIGIN)
|
||||
|
||||
self.play(*anims)
|
||||
self.wait()
|
||||
|
||||
anims = []
|
||||
for (guide, i, h) in zip(y_guides, prob_grid, y_guide_heights):
|
||||
new_y_guide_height = self.bar_anchor_height + i * self.scale_y * float(self.row.width)
|
||||
v = (new_y_guide_height - h) * UP
|
||||
anims.append(guide.shift)
|
||||
anims.append(v)
|
||||
|
||||
anims.append(self.bars.stretch_about_point)
|
||||
anims.append(self.scale_y)
|
||||
anims.append(1)
|
||||
anims.append(self.bars.get_bottom())
|
||||
anims.append(self.bars.outline.stretch_about_point)
|
||||
anims.append(self.scale_y)
|
||||
anims.append(1)
|
||||
anims.append(self.bars.get_bottom())
|
||||
|
||||
self.play(*anims)
|
||||
self.wait()
|
||||
|
||||
class MorphBrickRowIntoHistogram100(MorphBrickRowIntoHistogram20):
|
||||
CONFIG = {
|
||||
"level" : 100,
|
||||
"x_ticks": 20,
|
||||
"prob_ticks": 0.02
|
||||
}
|
||||
|
||||
class MorphBrickRowIntoHistogram500(MorphBrickRowIntoHistogram20):
|
||||
CONFIG = {
|
||||
"level" : 500,
|
||||
"x_ticks": 100,
|
||||
"prob_ticks": 0.01
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
|
||||
class ProbabilityDistributions(PiCreatureScene):
|
||||
|
||||
CONFIG = {
|
||||
"default_pi_creature_kwargs": {
|
||||
"color": MAROON_E,
|
||||
"flip_at_start": False,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
lag_ratio = 0.2
|
||||
run_time = 3
|
||||
|
||||
|
||||
# WEATHER FORECAST
|
||||
|
||||
|
||||
unit_rect = Rectangle(
|
||||
height = 3, width = 3
|
||||
).shift(DOWN)
|
||||
|
||||
p_rain = 0.23
|
||||
p_sun = 1 - p_rain
|
||||
opacity = 0.7
|
||||
|
||||
rain_rect = unit_rect.copy().stretch(p_rain, 0)
|
||||
rain_rect.align_to(unit_rect, LEFT)
|
||||
rain_rect.set_fill(color = BLUE, opacity = opacity)
|
||||
rain_rect.set_stroke(width = 0)
|
||||
|
||||
sun_rect = unit_rect.copy().stretch(p_sun, 0)
|
||||
sun_rect.next_to(rain_rect, RIGHT, buff = 0)
|
||||
sun_rect.set_fill(color = YELLOW, opacity = opacity)
|
||||
sun_rect.set_stroke(width = 0)
|
||||
|
||||
self.add(unit_rect, rain_rect, sun_rect)
|
||||
|
||||
rain = SVGMobject(file_name = "rain").scale(0.25)
|
||||
sun = SVGMobject(file_name = "sun").scale(0.35)
|
||||
|
||||
rain.flip().move_to(rain_rect)
|
||||
sun.move_to(sun_rect)
|
||||
|
||||
self.add(rain, sun)
|
||||
|
||||
|
||||
text_scale = 0.7
|
||||
|
||||
brace_rain = Brace(rain_rect, UP)
|
||||
p_rain_label = TextMobject("$P($rain$)=$").scale(text_scale)
|
||||
p_rain_decimal = DecimalNumber(p_rain).scale(text_scale)
|
||||
p_rain_decimal.next_to(p_rain_label)
|
||||
p_rain_whole_label = VGroup(p_rain_label, p_rain_decimal)
|
||||
p_rain_whole_label.next_to(brace_rain, UP)
|
||||
|
||||
brace_sun = Brace(sun_rect, DOWN)
|
||||
p_sun_label = TextMobject("$P($sunshine$)=$").scale(text_scale)
|
||||
p_sun_decimal = DecimalNumber(p_sun).scale(text_scale)
|
||||
p_sun_decimal.next_to(p_sun_label)
|
||||
p_sun_whole_label = VGroup(p_sun_label, p_sun_decimal)
|
||||
p_sun_whole_label.next_to(brace_sun, DOWN)
|
||||
|
||||
self.add(brace_rain, p_rain_whole_label, brace_sun, p_sun_whole_label)
|
||||
|
||||
self.wait(6)
|
||||
|
||||
|
||||
|
||||
# new_p_rain = 0.68
|
||||
# new_p_sun = 1 - new_p_rain
|
||||
|
||||
# new_rain_rect = unit_rect.copy().stretch(new_p_rain, 0)
|
||||
# new_rain_rect.align_to(unit_rect, LEFT)
|
||||
# new_rain_rect.set_fill(color = BLUE, opacity = opacity)
|
||||
# new_rain_rect.set_stroke(width = 0)
|
||||
|
||||
# new_sun_rect = unit_rect.copy().stretch(new_p_sun, 0)
|
||||
# new_sun_rect.next_to(new_rain_rect, RIGHT, buff = 0)
|
||||
# new_sun_rect.set_fill(color = YELLOW, opacity = opacity)
|
||||
# new_sun_rect.set_stroke(width = 0)
|
||||
|
||||
# new_rain = SVGMobject(file_name = "rain").scale(0.35)
|
||||
# new_sun = SVGMobject(file_name = "sun").scale(0.35)
|
||||
|
||||
# new_rain.flip().move_to(new_rain_rect)
|
||||
# new_sun.move_to(new_sun_rect)
|
||||
|
||||
# new_brace_rain = Brace(new_rain_rect, UP)
|
||||
# new_p_rain_label = TextMobject("$P($rain$)=$").scale(text_scale)
|
||||
# new_p_rain_decimal = DecimalNumber(new_p_rain).scale(text_scale)
|
||||
# new_p_rain_decimal.next_to(new_p_rain_label)
|
||||
# new_p_rain_whole_label = VGroup(new_p_rain_label, new_p_rain_decimal)
|
||||
# new_p_rain_whole_label.next_to(new_brace_rain, UP)
|
||||
|
||||
|
||||
# new_brace_sun = Brace(new_sun_rect, DOWN)
|
||||
# new_p_sun_label = TextMobject("$P($sunshine$)=$").scale(text_scale)
|
||||
# new_p_sun_decimal = DecimalNumber(new_p_sun).scale(text_scale)
|
||||
# new_p_sun_decimal.next_to(new_p_sun_label)
|
||||
# new_p_sun_whole_label = VGroup(new_p_sun_label, new_p_sun_decimal)
|
||||
# new_p_sun_whole_label.next_to(new_brace_sun, DOWN)
|
||||
|
||||
# def rain_update_func(alpha):
|
||||
# return alpha * new_p_rain + (1 - alpha) * p_rain
|
||||
|
||||
# def sun_update_func(alpha):
|
||||
# return 1 - rain_update_func(alpha)
|
||||
|
||||
# update_p_rain = ChangingDecimal(
|
||||
# p_rain_decimal, rain_update_func,
|
||||
# tracked_mobject = p_rain_label,
|
||||
# run_time = run_time
|
||||
# )
|
||||
# update_p_sun = ChangingDecimal(
|
||||
# p_sun_decimal, sun_update_func,
|
||||
# tracked_mobject = p_sun_label,
|
||||
# run_time = run_time
|
||||
# )
|
||||
|
||||
# self.play(
|
||||
# Transform(rain_rect, new_rain_rect, run_time = run_time),
|
||||
# Transform(sun_rect, new_sun_rect, run_time = run_time),
|
||||
# Transform(rain, new_rain, run_time = run_time),
|
||||
# Transform(sun, new_sun, run_time = run_time),
|
||||
# Transform(brace_rain, new_brace_rain, run_time = run_time),
|
||||
# Transform(brace_sun, new_brace_sun, run_time = run_time),
|
||||
# Transform(p_rain_label, new_p_rain_label, run_time = run_time),
|
||||
# Transform(p_sun_label, new_p_sun_label, run_time = run_time),
|
||||
# update_p_rain,
|
||||
# update_p_sun
|
||||
# )
|
||||
|
||||
|
||||
|
||||
# move the forecast into a corner
|
||||
|
||||
forecast = VGroup(
|
||||
rain_rect, sun_rect, rain, sun, brace_rain, brace_sun,
|
||||
p_rain_whole_label, p_sun_whole_label, unit_rect
|
||||
)
|
||||
|
||||
forecast.target = forecast.copy().scale(0.5)
|
||||
forecast.target.to_corner(UL)
|
||||
|
||||
self.play(MoveToTarget(forecast))
|
||||
|
||||
self.play(
|
||||
FadeOut(brace_rain),
|
||||
FadeOut(brace_sun),
|
||||
FadeOut(p_rain_whole_label),
|
||||
FadeOut(p_sun_whole_label),
|
||||
)
|
||||
|
||||
self.wait(3)
|
||||
|
||||
|
||||
# DOUBLE DICE THROW
|
||||
|
||||
cell_size = 0.5
|
||||
dice_table = TwoDiceTable(cell_size = cell_size, label_scale = 0.7)
|
||||
dice_table.shift(0.8 * DOWN)
|
||||
dice_unit_rect = SurroundingRectangle(
|
||||
dice_table.cells, buff = 0,
|
||||
stroke_color=WHITE
|
||||
)
|
||||
|
||||
dice_table_grouped_cells = VGroup()
|
||||
|
||||
for i in range(6):
|
||||
dice_table_grouped_cells.add(VGroup(*[
|
||||
VGroup(
|
||||
dice_table.cells[6 * i - 5 * k],
|
||||
dice_table.labels[6 * i - 5 * k],
|
||||
)
|
||||
for k in range(i + 1)
|
||||
]))
|
||||
|
||||
for i in range(5):
|
||||
dice_table_grouped_cells.add(VGroup(*[
|
||||
VGroup(
|
||||
dice_table.cells[31 + i - 5 * k],
|
||||
dice_table.labels[31 + i - 5 * k],
|
||||
)
|
||||
for k in range(5 - i)
|
||||
]))
|
||||
|
||||
# self.play(
|
||||
# FadeIn(dice_unit_rect),
|
||||
# FadeIn(dice_table.rows)
|
||||
# )
|
||||
|
||||
# for (cell, label) in zip(dice_table.cells, dice_table.labels):
|
||||
# cell.add(label)
|
||||
|
||||
# self.play(
|
||||
# LaggedStartMap(FadeIn, dice_table_grouped_cells,
|
||||
# lag_ratio = lag_ratio, run_time = run_time)
|
||||
# )
|
||||
self.play(
|
||||
FadeIn(dice_table_grouped_cells),
|
||||
FadeIn(dice_unit_rect),
|
||||
FadeIn(dice_table.rows)
|
||||
)
|
||||
|
||||
self.wait(3)
|
||||
|
||||
|
||||
self.play(
|
||||
dice_table_grouped_cells.space_out_submobjects, {"factor" : 1.5},
|
||||
rate_func=there_and_back_with_pause,
|
||||
run_time=run_time
|
||||
)
|
||||
|
||||
dice_table.add(dice_unit_rect)
|
||||
dice_table_target = dice_table.deepcopy()
|
||||
dice_table_target.scale(0.5)
|
||||
dice_table_target.to_corner(UR, buff=LARGE_BUFF)
|
||||
dice_table_target.shift(0.4 * UP)
|
||||
|
||||
self.play(Transform(dice_table, dice_table_target))
|
||||
|
||||
self.play(
|
||||
FadeOut(dice_table.rows),
|
||||
FadeOut(dice_unit_rect),
|
||||
)
|
||||
|
||||
self.wait(3)
|
||||
|
||||
# TITLE
|
||||
|
||||
text = TextMobject("Probability distributions")
|
||||
text.to_edge(UP)
|
||||
text_rect = SurroundingRectangle(text, buff=MED_SMALL_BUFF)
|
||||
text_rect.match_color(text)
|
||||
|
||||
self.play(
|
||||
FadeIn(text),
|
||||
ShowCreation(text_rect)
|
||||
)
|
||||
|
||||
self.wait(3)
|
||||
|
||||
|
||||
# COIN FLIP
|
||||
|
||||
|
||||
brick_row = BrickRow(3, height = 2, width = 10)
|
||||
coin_flip_rect = VGroup(brick_row)
|
||||
|
||||
tallies = VGroup()
|
||||
for (i, brick) in enumerate(brick_row.rects):
|
||||
tally = TallyStack(3 - i, i)
|
||||
tally.move_to(brick)
|
||||
tallies.add(tally)
|
||||
coin_flip_rect.add(tallies)
|
||||
|
||||
coin_flip_rect.scale(0.65).shift(RIGHT)
|
||||
self.play(FadeIn(coin_flip_rect))
|
||||
|
||||
counts = [1, 3, 3, 1]
|
||||
braces = VGroup()
|
||||
labels = VGroup()
|
||||
for (rect, count) in zip(brick_row.rects, counts):
|
||||
label = TexMobject("{" + str(count) + "\\over 8}").scale(0.5)
|
||||
brace = Brace(rect, DOWN)
|
||||
label.next_to(brace, DOWN)
|
||||
braces.add(brace)
|
||||
labels.add(label)
|
||||
|
||||
self.play(
|
||||
FadeIn(braces),
|
||||
FadeIn(labels)
|
||||
)
|
||||
|
||||
coin_flip_rect.add(braces, labels)
|
||||
|
||||
|
||||
self.wait(6)
|
||||
|
||||
outcomes = brick_row.get_outcome_rects_for_level(3, with_labels = True,
|
||||
inset = True)
|
||||
outcomes.scale(0.65)
|
||||
outcomes.move_to(brick_row.get_center())
|
||||
outcome_braces = VGroup(*[
|
||||
Brace(outcome, DOWN) for outcome in outcomes
|
||||
])
|
||||
outcome_labels = VGroup(*[
|
||||
TexMobject("{1\over 8}").scale(0.5).next_to(brace, DOWN)
|
||||
for brace in outcome_braces
|
||||
])
|
||||
|
||||
self.play(
|
||||
FadeOut(tallies),
|
||||
FadeIn(outcomes),
|
||||
FadeOut(braces),
|
||||
FadeOut(labels),
|
||||
FadeIn(outcome_braces),
|
||||
FadeIn(outcome_labels)
|
||||
)
|
||||
|
||||
|
||||
self.wait(10)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
from active_projects.eop.independence import *
|
||||
|
||||
from for_3b1b_videos.pi_class import PiCreatureClass
|
||||
|
||||
class QuizResult(PiCreatureScene):
|
||||
CONFIG = {
|
||||
"pi_creatures_start_on_screen" : False,
|
||||
"random_seed" : 0
|
||||
}
|
||||
def construct(self):
|
||||
|
||||
|
||||
def get_example_quiz():
|
||||
quiz = get_quiz(
|
||||
"Define ``Brachistochrone'' ",
|
||||
"Define ``Tautochrone'' ",
|
||||
"Define ``Cycloid'' ",
|
||||
)
|
||||
rect = SurroundingRectangle(quiz, buff = 0)
|
||||
rect.set_fill(color = BLACK, opacity = 1)
|
||||
rect.set_stroke(width = 0)
|
||||
quiz.add_to_back(rect)
|
||||
return quiz
|
||||
|
||||
|
||||
highlight_color = WHITE
|
||||
|
||||
nb_students_x = 5
|
||||
nb_students_y = 3
|
||||
spacing_students_x = 2.0
|
||||
spacing_students_y = 2.2
|
||||
|
||||
all_students = PiCreatureClass(
|
||||
width = nb_students_x, height = nb_students_y)# VGroup()
|
||||
student_points = []
|
||||
grades = []
|
||||
grades_count = []
|
||||
hist_y_values = np.zeros(4)
|
||||
for i in range(nb_students_x):
|
||||
for j in range(nb_students_y):
|
||||
x = i * spacing_students_x
|
||||
y = j * spacing_students_y
|
||||
#pi = PiCreature().scale(0.3)
|
||||
#pi.move_to([x,y,0])
|
||||
#all_students.add(pi)
|
||||
all_students[i*nb_students_y + j].move_to([x,y,0])
|
||||
q1 = np.random.choice([True, False])
|
||||
q2 = np.random.choice([True, False])
|
||||
q3 = np.random.choice([True, False])
|
||||
student_points.append([q1, q2, q3])
|
||||
grade = q1*1+q2*1+q3*1
|
||||
grades.append(grade)
|
||||
hist_y_values[grade] += 1
|
||||
# How many times has this grade already occured?
|
||||
grade_count = grades.count(grade)
|
||||
grades_count.append(grade_count)
|
||||
|
||||
|
||||
all_students.move_to(ORIGIN)
|
||||
self.pi_creatures = all_students
|
||||
self.play(FadeIn(all_students))
|
||||
|
||||
all_quizzes = VGroup()
|
||||
|
||||
quiz = get_example_quiz().scale(0.2)
|
||||
for pi in all_students:
|
||||
quiz_copy = quiz.copy()
|
||||
quiz_copy.next_to(pi, UP)
|
||||
all_quizzes.add(quiz_copy)
|
||||
|
||||
master_quiz = get_example_quiz()
|
||||
self.play(Write(master_quiz), run_time = 2)
|
||||
self.wait()
|
||||
self.play(ReplacementTransform(
|
||||
VGroup(master_quiz), all_quizzes,
|
||||
run_time=2,
|
||||
lag_ratio=0.5
|
||||
))
|
||||
self.wait(2)
|
||||
|
||||
grades_mob = VGroup()
|
||||
for (pi, quiz, grade) in zip(all_students, all_quizzes, grades):
|
||||
grade_mob = TexMobject(str(grade) + "/3")
|
||||
grade_mob.move_to(quiz)
|
||||
grades_mob.add(grade_mob)
|
||||
|
||||
self.remove(master_quiz)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(all_quizzes),
|
||||
FadeIn(grades_mob)
|
||||
)
|
||||
|
||||
# self.play(
|
||||
# all_students[2:].fade, 0.8,
|
||||
# grades_mob[2:].fade, 0.8
|
||||
# )
|
||||
|
||||
students_points_mob = VGroup()
|
||||
for (pi, quiz, points) in zip(all_students, all_quizzes, student_points):
|
||||
slot = get_slot_group(points, include_qs = False)
|
||||
slot.scale(0.5).move_to(quiz)
|
||||
students_points_mob.add(slot)
|
||||
|
||||
self.wait()
|
||||
self.play(
|
||||
#all_students.fade, 0,
|
||||
FadeOut(grades_mob),
|
||||
FadeIn(students_points_mob)
|
||||
)
|
||||
|
||||
all_students.save_state()
|
||||
students_points_mob.save_state()
|
||||
self.wait()
|
||||
randy = all_students[0]
|
||||
morty = all_students[nb_students_y]
|
||||
all_other_students = VGroup(*all_students)
|
||||
all_other_students.remove(randy, morty)
|
||||
randy_points = students_points_mob[0]
|
||||
morty_points = students_points_mob[nb_students_y]
|
||||
all_other_points = VGroup(*students_points_mob)
|
||||
all_other_points.remove(randy_points, morty_points)
|
||||
self.play(
|
||||
all_other_students.fade, 0.8,
|
||||
all_other_points.fade, 0.8,
|
||||
)
|
||||
self.wait()
|
||||
scale = 1.5
|
||||
self.play(randy_points.scale,scale)
|
||||
self.play(randy_points.scale,1.0/scale, morty_points.scale,scale)
|
||||
self.play(morty_points.scale,1.0/scale)
|
||||
|
||||
self.wait()
|
||||
self.play(
|
||||
all_students.restore,
|
||||
students_points_mob.restore,
|
||||
)
|
||||
|
||||
self.wait()
|
||||
anims = []
|
||||
for points in students_points_mob:
|
||||
anims.append(points.scale)
|
||||
anims.append(scale)
|
||||
self.play(*anims)
|
||||
|
||||
self.wait()
|
||||
anims = []
|
||||
for points in students_points_mob:
|
||||
anims.append(points.scale)
|
||||
anims.append(1.0/scale)
|
||||
self.play(*anims)
|
||||
|
||||
anims = []
|
||||
anchor_point = 3 * DOWN + 1 * LEFT
|
||||
for (pi, grade, grades_count) in zip(all_students, grades, grades_count):
|
||||
anims.append(pi.move_to)
|
||||
anims.append(anchor_point + grade * RIGHT + grades_count * UP)
|
||||
anims.append(FadeOut(students_points_mob))
|
||||
|
||||
self.wait()
|
||||
self.play(*anims)
|
||||
|
||||
grade_labels = VGroup()
|
||||
for i in range(4):
|
||||
grade_label = Integer(i, color = highlight_color)
|
||||
grade_label.move_to(i * RIGHT)
|
||||
grade_labels.add(grade_label)
|
||||
grade_labels.next_to(all_students, DOWN)
|
||||
out_of_label = TextMobject("out of 3", color = highlight_color)
|
||||
out_of_label.next_to(grade_labels, RIGHT, buff = MED_LARGE_BUFF)
|
||||
grade_labels.add(out_of_label)
|
||||
self.wait()
|
||||
self.play(Write(grade_labels))
|
||||
|
||||
grade_hist = Histogram(
|
||||
np.ones(4),
|
||||
hist_y_values,
|
||||
mode = "widths",
|
||||
x_labels = "none",
|
||||
y_label_position = "center",
|
||||
bar_stroke_width = 0,
|
||||
outline_stroke_width = 5
|
||||
)
|
||||
grade_hist.move_to(all_students)
|
||||
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeIn(grade_hist),
|
||||
FadeOut(all_students)
|
||||
)
|
||||
|
||||
|
||||
nb_students_label = TextMobject("\# of students", color = highlight_color)
|
||||
nb_students_label.move_to(5 * RIGHT + 1 * UP)
|
||||
arrows = VGroup(*[
|
||||
Arrow(nb_students_label.get_left(), grade_hist.bars[i].get_center(),
|
||||
color = highlight_color)
|
||||
for i in range(4)
|
||||
])
|
||||
self.wait()
|
||||
self.play(Write(nb_students_label), LaggedStartMap(GrowArrow,arrows))
|
||||
|
||||
percentage_label = TextMobject("\% of students", color = highlight_color)
|
||||
percentage_label.move_to(nb_students_label)
|
||||
percentages = hist_y_values / (nb_students_x * nb_students_y) * 100
|
||||
anims = []
|
||||
for (label, percentage) in zip(grade_hist.y_labels_group, percentages):
|
||||
new_label = DecimalNumber(percentage,
|
||||
num_decimal_places = 1,
|
||||
unit = "\%",
|
||||
color = highlight_color
|
||||
)
|
||||
new_label.scale(0.7)
|
||||
new_label.move_to(label)
|
||||
anims.append(Transform(label, new_label))
|
||||
anims.append(ReplacementTransform(nb_students_label, percentage_label))
|
||||
self.wait()
|
||||
self.play(*anims)
|
||||
|
||||
self.remove(all_quizzes)
|
||||
# put small copy of class in corner
|
||||
for (i,pi) in enumerate(all_students):
|
||||
x = i % 5
|
||||
y = i / 5
|
||||
pi.move_to(x * RIGHT + y * UP)
|
||||
all_students.scale(0.8)
|
||||
all_students.to_corner(DOWN + LEFT)
|
||||
self.wait()
|
||||
self.play(FadeIn(all_students))
|
||||
|
||||
prob_label = TextMobject("probability", color = highlight_color)
|
||||
prob_label.move_to(percentage_label)
|
||||
self.wait()
|
||||
self.play(
|
||||
all_students[8].set_color, MAROON_E,
|
||||
#all_students[:8].fade, 0.6,
|
||||
#all_students[9:].fade, 0.6,
|
||||
ReplacementTransform(percentage_label, prob_label)
|
||||
)
|
||||
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(prob_label),
|
||||
FadeOut(arrows)
|
||||
)
|
||||
|
||||
flash_hist = FlashThroughHistogram(
|
||||
grade_hist,
|
||||
direction = "vertical",
|
||||
mode = "random",
|
||||
cell_opacity = 0.5,
|
||||
run_time = 5,
|
||||
rate_func = linear
|
||||
)
|
||||
|
||||
flash_class = FlashThroughClass(
|
||||
all_students,
|
||||
mode = "random",
|
||||
highlight_color = MAROON_E,
|
||||
run_time = 5,
|
||||
rate_func = linear
|
||||
)
|
||||
|
||||
self.wait()
|
||||
for i in range(3):
|
||||
self.play(flash_hist, flash_class)
|
||||
self.remove(flash_hist.prototype_cell)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class ProbabilityRect(VMobject):
|
||||
CONFIG = {
|
||||
"unit_width" : 2,
|
||||
"unit_height" : 2,
|
||||
"alignment" : LEFT,
|
||||
"color": YELLOW,
|
||||
"opacity": 1.0,
|
||||
"num_decimal_places": 2,
|
||||
"use_percent" : False
|
||||
}
|
||||
|
||||
def __init__(self, p0, **kwargs):
|
||||
|
||||
VMobject.__init__(self, **kwargs)
|
||||
self.unit_rect = Rectangle(
|
||||
width = self.unit_width,
|
||||
height = self.unit_height,
|
||||
stroke_color = self.color
|
||||
)
|
||||
self.p = p0
|
||||
self.prob_rect = self.create_prob_rect(p0)
|
||||
self.prob_label = self.create_prob_label(p0)
|
||||
|
||||
self.add(self.unit_rect, self.prob_rect, self.prob_label)
|
||||
|
||||
|
||||
def create_prob_rect(self, p):
|
||||
|
||||
prob_width, prob_height = self.unit_width, self.unit_height
|
||||
|
||||
if self.alignment in [LEFT, RIGHT]:
|
||||
prob_width *= p
|
||||
elif self.alignment in [UP, DOWN]:
|
||||
prob_height *= p
|
||||
else:
|
||||
raise Exception("Aligment must be LEFT, RIGHT, UP or DOWN")
|
||||
|
||||
prob_rect = Rectangle(
|
||||
width = prob_width,
|
||||
height = prob_height,
|
||||
fill_color = self.color,
|
||||
fill_opacity = self.opacity,
|
||||
stroke_color = self.color
|
||||
)
|
||||
|
||||
prob_rect.align_to(self.unit_rect, direction = self.alignment)
|
||||
return prob_rect
|
||||
|
||||
|
||||
def create_prob_label(self, p):
|
||||
|
||||
if self.use_percent:
|
||||
prob_label = DecimalNumber(
|
||||
p * 100,
|
||||
color = BLACK,
|
||||
num_decimal_places = self.num_decimal_places,
|
||||
unit = "\%"
|
||||
)
|
||||
else:
|
||||
prob_label = DecimalNumber(
|
||||
p,
|
||||
color = BLACK,
|
||||
num_decimal_places = self.num_decimal_places,
|
||||
)
|
||||
|
||||
prob_label.move_to(self.prob_rect)
|
||||
|
||||
return prob_label
|
||||
|
||||
|
||||
class ChangeProbability(Animation):
|
||||
|
||||
def __init__(self, prob_mob, p1, **kwargs):
|
||||
|
||||
if not isinstance(prob_mob, ProbabilityRect):
|
||||
raise Exception("ChangeProportion's mobject must be a ProbabilityRect")
|
||||
|
||||
self.p1 = p1
|
||||
self.p0 = prob_mob.p
|
||||
Animation.__init__(self, prob_mob, **kwargs)
|
||||
|
||||
|
||||
def interpolate_mobject(self, alpha):
|
||||
|
||||
p = (1 - alpha) * self.p0 + alpha * self.p1
|
||||
self.mobject.remove(self.mobject.prob_rect, self.mobject.prob_label)
|
||||
self.mobject.prob_rect = self.mobject.create_prob_rect(p)
|
||||
self.mobject.prob_label = self.mobject.create_prob_label(p)
|
||||
self.mobject.add(self.mobject.prob_rect, self.mobject.prob_label)
|
||||
|
||||
|
||||
def clean_up_from_scene(self, scene=None):
|
||||
self.mobject.p = self.p1
|
||||
super(ChangeProbability, self).clean_up_from_scene(scene = scene)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ShowProbAsProportion(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
p0 = 0.3
|
||||
p1 = 1
|
||||
p2 = 0.18
|
||||
p3 = 0.64
|
||||
|
||||
prob_mob = ProbabilityRect(p0,
|
||||
unit_width = 4,
|
||||
unit_height = 2,
|
||||
use_percent = False,
|
||||
num_decimal_places = 2
|
||||
)
|
||||
|
||||
self.add(prob_mob)
|
||||
self.wait()
|
||||
self.play(
|
||||
ChangeProbability(prob_mob, p1,
|
||||
run_time = 3)
|
||||
)
|
||||
self.wait(0.5)
|
||||
self.play(
|
||||
ChangeProbability(prob_mob, p2,
|
||||
run_time = 3)
|
||||
)
|
||||
self.wait(0.5)
|
||||
self.play(
|
||||
ChangeProbability(prob_mob, p3,
|
||||
run_time = 3)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
|
||||
class ShowUncertaintyDarts(Scene):
|
||||
|
||||
|
||||
def throw_darts(self, n, run_time = 1):
|
||||
|
||||
points = np.random.normal(
|
||||
loc = self.dartboard.get_center(),
|
||||
scale = 0.6 * np.ones(3),
|
||||
size = (n,3)
|
||||
)
|
||||
points[:,2] = 0
|
||||
dots = VGroup()
|
||||
for point in points:
|
||||
dot = Dot(point, radius = 0.04, fill_opacity = 0.7)
|
||||
dots.add(dot)
|
||||
self.add(dot)
|
||||
|
||||
self.play(
|
||||
LaggedStartMap(FadeIn, dots, lag_ratio = 0.01, run_time = run_time)
|
||||
)
|
||||
|
||||
|
||||
def construct(self):
|
||||
|
||||
self.dartboard = ImageMobject("dartboard").scale(2)
|
||||
dartboard_circle = Circle(
|
||||
radius = self.dartboard.get_width() / 2,
|
||||
fill_color = BLACK,
|
||||
fill_opacity = 0.5,
|
||||
stroke_color = WHITE,
|
||||
stroke_width = 5
|
||||
)
|
||||
self.dartboard.add(dartboard_circle)
|
||||
|
||||
self.add(self.dartboard)
|
||||
|
||||
self.throw_darts(5,5)
|
||||
self.throw_darts(20,5)
|
||||
self.throw_darts(100,5)
|
||||
self.throw_darts(1000,5)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
class ShowUncertaintyDice(Scene):
|
||||
|
||||
def throw_a_die(self, run_time = 0.3):
|
||||
|
||||
eye = np.random.randint(1,7)
|
||||
face = self.row_of_dice.submobjects[eye - 1]
|
||||
|
||||
self.play(
|
||||
ApplyMethod(face.submobjects[0].set_fill, {"opacity": 1},
|
||||
rate_func = there_and_back,
|
||||
run_time = run_time,
|
||||
),
|
||||
)
|
||||
|
||||
def construct(self):
|
||||
|
||||
self.row_of_dice = RowOfDice(direction = DOWN).scale(0.5)
|
||||
self.add(self.row_of_dice)
|
||||
|
||||
for i in range(5):
|
||||
self.throw_a_die()
|
||||
self.wait(1)
|
||||
|
||||
for i in range(10):
|
||||
self.throw_a_die()
|
||||
self.wait(0.3)
|
||||
|
||||
for i in range(100):
|
||||
self.throw_a_die(0.05)
|
||||
self.wait(0.0)
|
||||
|
||||
|
||||
|
||||
class IdealizedDieHistogram(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
self.probs = 1.0/6 * np.ones(6)
|
||||
x_scale = 1.3
|
||||
|
||||
y_labels = ["${1\over 6}$"] * 6
|
||||
|
||||
hist = Histogram(np.ones(6), self.probs,
|
||||
mode = "widths",
|
||||
x_labels = "none",
|
||||
y_labels = y_labels,
|
||||
y_label_position = "center",
|
||||
y_scale = 20,
|
||||
x_scale = x_scale,
|
||||
)
|
||||
hist.rotate(-TAU/4)
|
||||
|
||||
for label in hist.y_labels_group:
|
||||
label.rotate(TAU/4)
|
||||
hist.remove(hist.y_labels_group)
|
||||
|
||||
|
||||
self.play(FadeIn(hist))
|
||||
self.play(LaggedStartMap(FadeIn, hist.y_labels_group))
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
|
||||
|
||||
class RandyIsSickOrNot(Scene):
|
||||
|
||||
|
||||
def construct(self):
|
||||
title = TextMobject("1 in 200")
|
||||
title.to_edge(UP)
|
||||
|
||||
|
||||
randy = SicklyPiCreature()
|
||||
randy.set_height(3)
|
||||
randy.move_to(2*LEFT)
|
||||
randy.change_mode("plain")
|
||||
randy.set_color(BLUE)
|
||||
randy.save_state()
|
||||
|
||||
self.add(randy)
|
||||
|
||||
p_sick = TexMobject("p(","\\text{sick}",") = 0.5\%").scale(1.7)
|
||||
p_sick.set_color_by_tex("sick", SICKLY_GREEN)
|
||||
p_sick.next_to(randy, UP, buff = LARGE_BUFF)
|
||||
self.add(p_sick)
|
||||
self.wait()
|
||||
|
||||
self.play(
|
||||
ApplyMethod(randy.get_slightly_sick, rate_func = there_and_back)
|
||||
)
|
||||
self.play(Blink(randy))
|
||||
self.wait(2)
|
||||
|
||||
self.play(
|
||||
ApplyMethod(randy.get_sick)
|
||||
)
|
||||
|
||||
self.play(Blink(randy))
|
||||
self.wait()
|
||||
|
||||
self.play(randy.get_better)
|
||||
|
||||
self.play(
|
||||
ApplyMethod(randy.get_slightly_sick, rate_func = there_and_back)
|
||||
)
|
||||
self.play(Blink(randy))
|
||||
self.wait(0.5)
|
||||
|
||||
self.play(
|
||||
ApplyMethod(randy.get_sick)
|
||||
)
|
||||
|
||||
self.play(Blink(randy))
|
||||
self.play(randy.get_better)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
|
||||
class OneIn200HasDisease(Scene):
|
||||
def construct(self):
|
||||
title = TextMobject("1 in 200")
|
||||
title.to_edge(UP)
|
||||
creature = PiCreature()
|
||||
|
||||
all_creatures = VGroup(*[
|
||||
VGroup(*[
|
||||
creature.copy()
|
||||
for y in range(20)
|
||||
]).arrange(DOWN, SMALL_BUFF)
|
||||
for x in range(10)
|
||||
]).arrange(RIGHT, SMALL_BUFF)
|
||||
all_creatures.set_height(FRAME_HEIGHT * 0.8)
|
||||
all_creatures.next_to(title, DOWN)
|
||||
randy = all_creatures[0][0]
|
||||
all_creatures[0].remove(randy)
|
||||
randy.change_mode("sick")
|
||||
randy.set_color(SICKLY_GREEN)
|
||||
randy.save_state()
|
||||
randy.set_height(3)
|
||||
randy.center()
|
||||
randy.change_mode("plain")
|
||||
randy.set_color(BLUE)
|
||||
|
||||
self.add(randy)
|
||||
|
||||
#p_sick = TexMobject("p(","\\text{sick}",") = 0.5\%")
|
||||
#p_sick.set_color_by_tex("sick", SICKLY_GREEN)
|
||||
#p_sick.next_to(randy, RIGHT+UP)
|
||||
#self.add(p_sick)
|
||||
self.wait()
|
||||
|
||||
self.play(
|
||||
randy.change_mode, "sick",
|
||||
randy.set_color, SICKLY_GREEN
|
||||
)
|
||||
self.play(Blink(randy))
|
||||
self.play(randy.restore)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(title),
|
||||
LaggedStartMap(FadeIn, all_creatures, run_time = 3)
|
||||
)
|
||||
self.wait()
|
||||
@@ -1,29 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
|
||||
class StackingCoins(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
h = t = 0
|
||||
heads_stack = HeadsStack(size = h)
|
||||
heads_stack.next_to(0.5*LEFT + 3*DOWN, UP)
|
||||
tails_stack = TailsStack(size = t)
|
||||
tails_stack.next_to(0.5*RIGHT + 3*DOWN, UP)
|
||||
self.add(heads_stack, tails_stack)
|
||||
|
||||
for i in range(120):
|
||||
flip = np.random.choice(["H", "T"])
|
||||
if flip == "H":
|
||||
h += 1
|
||||
new_heads_stack = HeadsStack(size = h)
|
||||
new_heads_stack.next_to(0.5*LEFT + 3*DOWN, UP)
|
||||
self.play(Transform(heads_stack, new_heads_stack,
|
||||
run_time = 0.2))
|
||||
elif flip == "T":
|
||||
t += 1
|
||||
new_tails_stack = TailsStack(size = t)
|
||||
new_tails_stack.next_to(0.5*RIGHT + 3*DOWN, UP)
|
||||
self.play(Transform(tails_stack, new_tails_stack,
|
||||
run_time = 0.2))
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
|
||||
class RandyThinksAboutCoin(PiCreatureScene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
randy = self.get_primary_pi_creature()
|
||||
randy.center()
|
||||
self.add(randy)
|
||||
self.wait()
|
||||
h_or_t = BinaryOption(UprightHeads().scale(3), UprightTails().scale(3),
|
||||
text_scale = 1.5)
|
||||
self.think(h_or_t, direction = LEFT)
|
||||
|
||||
v = 0.3
|
||||
self.play(
|
||||
h_or_t[0].shift,v*UP,
|
||||
h_or_t[2].shift,v*DOWN,
|
||||
)
|
||||
self.play(
|
||||
h_or_t[0].shift,2*v*DOWN,
|
||||
h_or_t[2].shift,2*v*UP,
|
||||
)
|
||||
self.play(
|
||||
h_or_t[0].shift,v*UP,
|
||||
h_or_t[2].shift,v*DOWN,
|
||||
)
|
||||
|
||||
self.wait()
|
||||
@@ -1,132 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusable_imports import *
|
||||
from active_projects.eop.combinations import *
|
||||
from active_projects.eop.independence import *
|
||||
|
||||
import itertools as it
|
||||
|
||||
class RandyFlipsAndStacks(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
randy = CoinFlippingPiCreature(color = MAROON_E)
|
||||
randy.scale(0.5).to_edge(LEFT + DOWN)
|
||||
|
||||
heads = tails = 0
|
||||
tally = TallyStack(heads, tails, anchor = ORIGIN)
|
||||
|
||||
nb_flips = 10
|
||||
|
||||
flips = np.random.randint(2, size = nb_flips)
|
||||
|
||||
for i in range(nb_flips):
|
||||
|
||||
self.play(FlipCoin(randy))
|
||||
self.wait(0.5)
|
||||
|
||||
flip = flips[i]
|
||||
if flip == 0:
|
||||
heads += 1
|
||||
elif flip == 1:
|
||||
tails += 1
|
||||
else:
|
||||
raise Exception("That side does not exist on this coin")
|
||||
|
||||
new_tally = TallyStack(heads, tails, anchor = ORIGIN)
|
||||
|
||||
if tally.nb_heads == 0 and new_tally.nb_heads == 1:
|
||||
self.play(FadeIn(new_tally.heads_stack))
|
||||
elif tally.nb_tails == 0 and new_tally.nb_tails == 1:
|
||||
self.play(FadeIn(new_tally.tails_stack))
|
||||
else:
|
||||
self.play(Transform(tally, new_tally))
|
||||
|
||||
tally = new_tally
|
||||
|
||||
|
||||
|
||||
class TwoDiceTableScene(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
table = TwoDiceTable(cell_size = 1)
|
||||
|
||||
table.center()
|
||||
self.add(table)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class VisualCovariance(Scene):
|
||||
|
||||
|
||||
def construct(self):
|
||||
|
||||
size = 4
|
||||
square = Square(side_length = size)
|
||||
n_points = 30
|
||||
cloud = VGroup(*[
|
||||
Dot((x + 0.8*y) * RIGHT + y * UP).set_fill(WHITE, 1)
|
||||
for x, y in zip(
|
||||
np.random.normal(0, 1, n_points),
|
||||
np.random.normal(0, 1, n_points)
|
||||
)
|
||||
])
|
||||
self.add_foreground_mobject(cloud)
|
||||
|
||||
x_axis = Vector(8*RIGHT, color = WHITE).move_to(2.5*DOWN)
|
||||
y_axis = Vector(5*UP, color = WHITE).move_to(4*LEFT)
|
||||
|
||||
self.add(x_axis, y_axis)
|
||||
|
||||
|
||||
random_pairs = [ (p1, p2) for (p1, p2) in
|
||||
it.combinations(cloud, 2)
|
||||
]
|
||||
np.random.shuffle(random_pairs)
|
||||
|
||||
|
||||
|
||||
for (p1, p2) in random_pairs:
|
||||
c1, c2 = p1.get_center(), p2.get_center()
|
||||
x1, y1, x2, y2 = c1[0], c1[1], c2[0], c2[1]
|
||||
if x1 >= x2:
|
||||
continue
|
||||
if y2 > y1:
|
||||
# make a red rect
|
||||
color = RED
|
||||
opacity = 0.1
|
||||
|
||||
elif y2 < y1:
|
||||
# make a blue rect
|
||||
color = BLUE
|
||||
opacity = 0.2
|
||||
|
||||
rect = Rectangle(width = x2 - x1, height = abs(y2 - y1))
|
||||
rect.set_fill(color = color, opacity = opacity)
|
||||
rect.set_stroke(width = 0)
|
||||
rect.move_to((c1+c2)/2)
|
||||
|
||||
self.play(FadeIn(rect), run_time = 0.05)
|
||||
|
||||
|
||||
|
||||
|
||||
class BinaryChoices(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
example1 = BinaryOption(UprightHeads(), UprightTails())
|
||||
example2 = BinaryOption(Male(), Female())
|
||||
example3 = BinaryOption(Checkmark(), Xmark())
|
||||
|
||||
example2.next_to(example1, DOWN, buff = MED_LARGE_BUFF)
|
||||
example3.next_to(example2, DOWN, buff = MED_LARGE_BUFF)
|
||||
|
||||
all = VGroup(example1, example2, example3)
|
||||
all = all.scale(2)
|
||||
|
||||
self.play(
|
||||
LaggedStartMap(FadeIn, all)
|
||||
)
|
||||
@@ -1,43 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class WhatDoesItReallyMean(TeacherStudentsScene):
|
||||
CONFIG = {
|
||||
"default_pi_creature_kwargs": {
|
||||
"color": MAROON_E,
|
||||
"flip_at_start": True,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
student_q = TextMobject(
|
||||
"What does", "``probability''\\\\",
|
||||
"\\emph{actually}", "mean?"
|
||||
)
|
||||
student_q.set_color_by_tex("probability", YELLOW)
|
||||
self.student_says(student_q, target_mode="sassy")
|
||||
self.wait()
|
||||
self.play(
|
||||
self.students[1].change_mode, "confused"
|
||||
)
|
||||
self.wait(2)
|
||||
student_bubble = self.students[1].bubble
|
||||
self.students[1].bubble = None
|
||||
student_bubble.add(student_bubble.content)
|
||||
self.play(
|
||||
student_bubble.scale, 0.5,
|
||||
student_bubble.to_corner, UL,
|
||||
)
|
||||
self.teacher_says(
|
||||
"Don't worry -- philosophy\\\\ can come later!",
|
||||
added_anims=[self.get_student_changes(*3 * ["happy"])],
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(RemovePiCreatureBubble(self.teacher))
|
||||
self.play(*[
|
||||
ApplyMethod(pi.look_at, ORIGIN) for pi in self.get_pi_creatures()
|
||||
])
|
||||
self.change_all_student_modes("pondering", look_at_arg=UP)
|
||||
self.wait(3)
|
||||
self.change_student_modes("confused", look_at_arg=UP)
|
||||
self.wait(3)
|
||||
@@ -1,103 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
def print_permutation(index_list):
|
||||
|
||||
|
||||
n = max(max(index_list), len(index_list))
|
||||
for i in range(0,n):
|
||||
if index_list[i] > n - i:
|
||||
raise Exception("Impossible indices!")
|
||||
|
||||
#print "given index list:", index_list
|
||||
perm_list = n * ["_"]
|
||||
alphabet = ["A", "B", "C", "D", "E", "F",
|
||||
"G", "H", "I", "J", "K", "L",
|
||||
"M", "N", "O", "P", "Q", "R",
|
||||
"S", "T", "U", "V", "W", "X",
|
||||
"Y", "Z"]
|
||||
free_indices = list(range(n))
|
||||
free_indices_p1 = list(range(1,n + 1))
|
||||
#print perm_list
|
||||
for i in range(n):
|
||||
findex = index_list[i] - 1
|
||||
#print "place next letter at", findex + 1, "th free place"
|
||||
tindex = free_indices[findex]
|
||||
#print "so at position", tindex + 1
|
||||
perm_list[tindex] = alphabet[i]
|
||||
free_indices.remove(tindex)
|
||||
free_indices_p1.remove(tindex + 1)
|
||||
#print "remaining free places:", free_indices_p1
|
||||
#print perm_list
|
||||
|
||||
return "".join(perm_list)
|
||||
|
||||
|
||||
class PermutationGrid(Scene):
|
||||
|
||||
def text_box(self, str):
|
||||
box = TextMobject(str).scale(0.3)
|
||||
box.add(SurroundingRectangle(box, stroke_color = DARK_GREY))
|
||||
return box
|
||||
|
||||
|
||||
def construct(self):
|
||||
|
||||
|
||||
N = 5
|
||||
|
||||
index_list = []
|
||||
perm5_box = VGroup()
|
||||
for i in range(1, N + 1):
|
||||
index_list.append(i)
|
||||
perm4_box = VGroup()
|
||||
for j in range(1, N):
|
||||
index_list.append(j)
|
||||
perm3_box = VGroup()
|
||||
for k in range(1, N - 1):
|
||||
index_list.append(k)
|
||||
perm2_box = VGroup()
|
||||
for l in range(1, N - 2):
|
||||
index_list.append(l)
|
||||
index_list.append(1)
|
||||
perm_box = self.text_box(print_permutation(index_list))
|
||||
if l > 1:
|
||||
perm_box.next_to(perm2_box[-1], DOWN, buff = 0)
|
||||
perm2_box.add(perm_box)
|
||||
index_list.pop()
|
||||
index_list.pop()
|
||||
if k > 1:
|
||||
perm2_box.next_to(perm3_box[-1], RIGHT, buff = 0.08)
|
||||
perm3_box.add(perm2_box)
|
||||
index_list.pop()
|
||||
perm3_box.add(SurroundingRectangle(perm3_box, buff = 0.12, stroke_color = LIGHT_GRAY))
|
||||
if j > 1:
|
||||
perm3_box.next_to(perm4_box[-1], DOWN, buff = 0)
|
||||
perm4_box.add(perm3_box)
|
||||
index_list.pop()
|
||||
if i > 1:
|
||||
perm4_box.next_to(perm5_box[-1], RIGHT, buff = 0.16)
|
||||
perm5_box.add(perm4_box)
|
||||
index_list.pop()
|
||||
|
||||
perm5_box.move_to(ORIGIN)
|
||||
self.add(perm5_box)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,347 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
from once_useful_constructs.combinatorics import *
|
||||
|
||||
nb_levels = 5
|
||||
|
||||
dev_x_step = 2
|
||||
dev_y_step = 5
|
||||
|
||||
GRADE_COLOR_1 = RED
|
||||
GRADE_COLOR_2 = BLUE
|
||||
|
||||
|
||||
|
||||
def graded_square(n,k):
|
||||
return Square(
|
||||
side_length = 1,
|
||||
fill_color = graded_color(n,k),
|
||||
fill_opacity = 1,
|
||||
stroke_width = 1
|
||||
)
|
||||
|
||||
def graded_binomial(n,k):
|
||||
return Integer(
|
||||
choose(n,k),
|
||||
color = graded_color(n,k)
|
||||
)
|
||||
|
||||
def split_square(n,k):
|
||||
width = 1
|
||||
height = 1
|
||||
|
||||
proportion = float(choose(n,k)) / 2**n
|
||||
|
||||
lower_height = proportion * height
|
||||
upper_height = (1 - proportion) * height
|
||||
lower_rect = Rectangle(
|
||||
width = width,
|
||||
height = lower_height,
|
||||
fill_color = RED,
|
||||
fill_opacity = 1.0,
|
||||
stroke_color = WHITE,
|
||||
stroke_width = 3
|
||||
)
|
||||
upper_rect = Rectangle(
|
||||
width = width,
|
||||
height = upper_height,
|
||||
fill_color = BLUE,
|
||||
fill_opacity = 1.0,
|
||||
stroke_color = WHITE,
|
||||
stroke_width = 3
|
||||
)
|
||||
upper_rect.next_to(lower_rect,UP,buff = 0)
|
||||
square = VGroup(lower_rect, upper_rect).move_to(ORIGIN)
|
||||
return square
|
||||
|
||||
|
||||
class BuildNewPascalRow(Transform):
|
||||
|
||||
def __init__(self,mobject, duplicate_row = None, **kwargs):
|
||||
if mobject.__class__ != GeneralizedPascalsTriangle and mobject.__class__ != PascalsTriangle:
|
||||
raise("Transform BuildNewPascalRow only works on members of (Generalized)PascalsTriangle!")
|
||||
|
||||
n = mobject.nrows - 1
|
||||
lowest_row_copy1 = mobject.get_lowest_row()
|
||||
lowest_row_copy2 = duplicate_row
|
||||
|
||||
start_mob = VGroup(lowest_row_copy1, lowest_row_copy2)
|
||||
|
||||
new_pt = mobject.copy()
|
||||
new_pt.nrows += 1
|
||||
new_pt.generate_points()
|
||||
# align with original (copy got centered on screen)
|
||||
c1 = new_pt.coords_to_mobs[0][0].get_center()
|
||||
c2 = mobject.coords_to_mobs[0][0].get_center()
|
||||
print(c1, c2)
|
||||
v = c2 - c1
|
||||
new_pt.shift(v)
|
||||
|
||||
new_row_left_copy = VGroup(*[
|
||||
new_pt.coords_to_mobs[n+1][k]
|
||||
for k in range(0,n+1)
|
||||
])
|
||||
|
||||
new_row_right_copy = VGroup(*[
|
||||
new_pt.coords_to_mobs[n+1][k]
|
||||
for k in range(1,n+2)
|
||||
]).copy()
|
||||
|
||||
target_mob = VGroup(new_row_left_copy, new_row_right_copy)
|
||||
|
||||
Transform.__init__(self, start_mob, target_mob, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class SimplePascal(Scene):
|
||||
|
||||
def build_new_pascal_row(self,old_pt):
|
||||
|
||||
lowest_row_copy = old_pt.get_lowest_row().copy()
|
||||
self.add(lowest_row_copy)
|
||||
|
||||
n = old_pt.nrows - 1
|
||||
lowest_row_copy1 = old_pt.get_lowest_row()
|
||||
lowest_row_copy2 = lowest_row_copy1.copy()
|
||||
|
||||
|
||||
start_mob = VGroup(lowest_row_copy1, lowest_row_copy2)
|
||||
self.add(start_mob)
|
||||
|
||||
new_pt = old_pt.copy()
|
||||
cell_height = old_pt.height / old_pt.nrows
|
||||
cell_width = old_pt.width / old_pt.nrows
|
||||
new_pt.nrows += 1
|
||||
new_pt.height = new_pt.nrows * cell_height
|
||||
new_pt.width = new_pt.nrows * cell_width
|
||||
|
||||
new_pt.generate_points()
|
||||
# align with original (copy got centered on screen)
|
||||
c1 = new_pt.coords_to_mobs[0][0].get_center()
|
||||
c2 = old_pt.coords_to_mobs[0][0].get_center()
|
||||
v = c2 - c1
|
||||
new_pt.shift(v)
|
||||
|
||||
new_row_left_copy = VGroup(*[
|
||||
new_pt.coords_to_mobs[n+1][k]
|
||||
for k in range(0,n+1)
|
||||
])
|
||||
|
||||
new_row_right_copy = VGroup(*[
|
||||
new_pt.coords_to_mobs[n+1][k]
|
||||
for k in range(1,n+2)
|
||||
]).copy()
|
||||
|
||||
target_mob = VGroup(new_row_left_copy, new_row_right_copy)
|
||||
self.play(Transform(start_mob, target_mob))
|
||||
|
||||
return new_pt
|
||||
|
||||
|
||||
|
||||
def construct(self):
|
||||
|
||||
cell_height = 1
|
||||
cell_width = 1
|
||||
nrows = 1
|
||||
pt = GeneralizedPascalsTriangle(
|
||||
nrows = nrows,
|
||||
height = nrows * cell_height,
|
||||
width = nrows * cell_width,
|
||||
submob_class = graded_square,
|
||||
portion_to_fill = 0.9
|
||||
)
|
||||
pt.shift(3 * UP)
|
||||
self.add(pt)
|
||||
lowest_row_copy = pt.get_lowest_row().copy()
|
||||
self.add(lowest_row_copy)
|
||||
#self.play(BuildNewPascalRow(pt, duplicate_row = lowest_row_copy))
|
||||
for i in range(7):
|
||||
pt = self.build_new_pascal_row(pt)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class PascalNetScene(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
unit_width = 0.25
|
||||
top_height = 4.0
|
||||
level_height = 2.0 * top_height / nb_levels
|
||||
|
||||
start_points = np.array([top_height * UP])
|
||||
|
||||
dev_start = start_points[0]
|
||||
|
||||
j = 0
|
||||
|
||||
for n in range(nb_levels):
|
||||
|
||||
half_width = 0.5 * (n + 0.5) * unit_width
|
||||
|
||||
stop_points_left = start_points.copy()
|
||||
stop_points_left[:,0] -= 0.5 * unit_width
|
||||
stop_points_left[:,1] -= level_height
|
||||
|
||||
stop_points_right = start_points.copy()
|
||||
stop_points_right[:,0] += 0.5 * unit_width
|
||||
stop_points_right[:,1] -= level_height
|
||||
|
||||
for (p,q) in zip(start_points,stop_points_left):
|
||||
alpha = np.abs((p[0]+q[0])/2) / half_width
|
||||
color = rainbow_color(alpha)
|
||||
line = Line(p,q, stroke_color = color)
|
||||
self.add(line)
|
||||
|
||||
for (i,(p,q)) in enumerate(zip(start_points,stop_points_right)):
|
||||
alpha = np.abs((p[0]+q[0])/2) / half_width
|
||||
color = rainbow_color(alpha)
|
||||
line = Line(p,q, stroke_color = color)
|
||||
self.add(line)
|
||||
|
||||
if (n + 1) % dev_y_step == 0 and n != 1:
|
||||
j += dev_x_step
|
||||
dev_stop = stop_points_left[j]
|
||||
line = Line(dev_start,dev_stop,stroke_color = WHITE)
|
||||
self.add(line)
|
||||
dot = Dot(dev_stop, fill_color = WHITE)
|
||||
self.add_foreground_mobject(dot)
|
||||
dev_start = dev_stop
|
||||
|
||||
start_points = np.append(stop_points_left,[stop_points_right[-1]], axis = 0)
|
||||
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
|
||||
class RescaledPascalNetScene(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
half_width = 3.0
|
||||
top_height = 4.0
|
||||
level_height = 2.0 * top_height / nb_levels
|
||||
|
||||
start_points = np.array([top_height * UP])
|
||||
left_edge = top_height * UP + half_width * LEFT
|
||||
right_edge = top_height * UP + half_width * RIGHT
|
||||
|
||||
dev_start = start_points[0]
|
||||
|
||||
j = 0
|
||||
|
||||
for n in range(nb_levels):
|
||||
|
||||
if n == 0:
|
||||
start_points_left_shift = np.array([left_edge])
|
||||
else:
|
||||
start_points_left_shift = start_points[:-1]
|
||||
start_points_left_shift = np.insert(start_points_left_shift,0,left_edge, axis = 0)
|
||||
stop_points_left = 0.5 * (start_points + start_points_left_shift)
|
||||
stop_points_left += level_height * DOWN
|
||||
|
||||
|
||||
if n == 0:
|
||||
start_points_right_shift = np.array([right_edge])
|
||||
else:
|
||||
start_points_right_shift = start_points[1:]
|
||||
start_points_right_shift = np.append(start_points_right_shift,np.array([right_edge]), axis = 0)
|
||||
stop_points_right = 0.5 * (start_points + start_points_right_shift)
|
||||
stop_points_right += level_height * DOWN
|
||||
|
||||
|
||||
for (i,(p,q)) in enumerate(zip(start_points,stop_points_left)):
|
||||
|
||||
color = LIGHT_GRAY
|
||||
|
||||
if n % 2 == 0 and i <= n/2:
|
||||
m = n/2 + 0.25
|
||||
jj = i
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
elif n % 2 == 0 and i > n/2:
|
||||
m = n/2 + 0.25
|
||||
jj = n - i + 0.5
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
elif n % 2 == 1 and i <= n/2:
|
||||
m = n/2 + 0.75
|
||||
jj = i
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
elif n % 2 == 1 and i > n/2:
|
||||
m = n/2 + 0.75
|
||||
jj = n - i + 0.5
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
line = Line(p,q, stroke_color = color)
|
||||
self.add(line)
|
||||
|
||||
for (i,(p,q)) in enumerate(zip(start_points,stop_points_right)):
|
||||
|
||||
color = LIGHT_GRAY
|
||||
|
||||
if n % 2 == 0 and i < n/2:
|
||||
m = n/2 + 0.25
|
||||
jj = i + 0.5
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
elif n % 2 == 0 and i >= n/2:
|
||||
m = n/2 + 0.25
|
||||
jj = n - i
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
elif n % 2 == 1 and i <= n/2:
|
||||
m = n/2 + 0.75
|
||||
jj = i + 0.5
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
elif n % 2 == 1 and i > n/2:
|
||||
m = n/2 + 0.75
|
||||
jj = n - i
|
||||
alpha = 1 - float(jj)/m
|
||||
color = rainbow_color(alpha)
|
||||
|
||||
|
||||
line = Line(p,q, stroke_color = color)
|
||||
self.add(line)
|
||||
|
||||
if (n + 1) % dev_y_step == 0 and n != 1:
|
||||
j += dev_x_step
|
||||
dev_stop = stop_points_left[j]
|
||||
line = Line(dev_start,dev_stop,stroke_color = WHITE)
|
||||
self.add(line)
|
||||
dot = Dot(dev_stop, fill_color = WHITE)
|
||||
self.add_foreground_mobject(dot)
|
||||
dev_start = dev_stop
|
||||
|
||||
|
||||
|
||||
start_points = np.append(stop_points_left,[stop_points_right[-1]], axis = 0)
|
||||
|
||||
left_edge += level_height * DOWN
|
||||
right_edge += level_height * DOWN
|
||||
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
from active_projects.eop.reusables.binary_option import *
|
||||
from active_projects.eop.reusables.brick_row import *
|
||||
from active_projects.eop.reusables.coin_flip_tree import *
|
||||
from active_projects.eop.reusables.coin_flipping_pi_creature import *
|
||||
from active_projects.eop.reusables.coin_stacks import *
|
||||
from active_projects.eop.reusables.dice import *
|
||||
from active_projects.eop.reusables.eop_constants import *
|
||||
from active_projects.eop.reusables.eop_helpers import *
|
||||
from active_projects.eop.reusables.histograms import *
|
||||
from active_projects.eop.reusables.sick_pi_creature import *
|
||||
from active_projects.eop.reusables.upright_coins import *
|
||||
@@ -1,15 +0,0 @@
|
||||
from mobject.types.vectorized_mobject import *
|
||||
from mobject.svg.tex_mobject import *
|
||||
|
||||
class BinaryOption(VMobject):
|
||||
CONFIG = {
|
||||
"text_scale" : 0.5
|
||||
}
|
||||
|
||||
def __init__(self, mob1, mob2, **kwargs):
|
||||
|
||||
VMobject.__init__(self, **kwargs)
|
||||
text = TextMobject("or").scale(self.text_scale)
|
||||
mob1.next_to(text, LEFT)
|
||||
mob2.next_to(text, RIGHT)
|
||||
self.add(mob1, text, mob2)
|
||||
@@ -1,208 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.eop.reusables.eop_helpers import *
|
||||
from active_projects.eop.reusables.eop_constants import *
|
||||
from active_projects.eop.reusables.upright_coins import *
|
||||
|
||||
class BrickRow(VMobject):
|
||||
|
||||
CONFIG = {
|
||||
"left_color" : COLOR_HEADS,
|
||||
"right_color" : COLOR_TAILS,
|
||||
"height" : 1.0,
|
||||
"width" : 8.0,
|
||||
"outcome_shrinkage_factor_x" : 0.95,
|
||||
"outcome_shrinkage_factor_y" : 0.94
|
||||
}
|
||||
|
||||
def __init__(self, n, **kwargs):
|
||||
self.subdiv_level = n
|
||||
self.coloring_level = n
|
||||
VMobject.__init__(self, **kwargs)
|
||||
|
||||
|
||||
def generate_points(self):
|
||||
|
||||
self.submobjects = []
|
||||
self.rects = self.get_rects_for_level(self.coloring_level)
|
||||
self.add(self.rects)
|
||||
self.subdivs = self.get_subdivs_for_level(self.subdiv_level)
|
||||
self.add(self.subdivs)
|
||||
|
||||
self.border = SurroundingRectangle(self,
|
||||
buff = 0, color = WHITE)
|
||||
self.add(self.border)
|
||||
|
||||
|
||||
|
||||
def get_rects_for_level(self,r):
|
||||
rects = VGroup()
|
||||
for k in range(r + 1):
|
||||
proportion = float(choose(r,k)) / 2**r
|
||||
new_rect = Rectangle(
|
||||
width = proportion * self.width,
|
||||
height = self.height,
|
||||
fill_color = graded_color(r,k),
|
||||
fill_opacity = 1,
|
||||
stroke_width = 0
|
||||
)
|
||||
if len(rects.submobjects) > 0:
|
||||
new_rect.next_to(rects,RIGHT,buff = 0)
|
||||
else:
|
||||
new_rect.next_to(self.get_center() + 0.5 * self.width * LEFT, RIGHT, buff = 0)
|
||||
rects.add(new_rect)
|
||||
return rects
|
||||
|
||||
|
||||
def get_subdivs_for_level(self,r):
|
||||
subdivs = VGroup()
|
||||
x = - 0.5 * self.width
|
||||
for k in range(0, r):
|
||||
proportion = float(choose(r,k)) / 2**r
|
||||
x += proportion * self.width
|
||||
subdiv = Line(
|
||||
x * RIGHT + 0.5 * self.height * UP,
|
||||
x * RIGHT + 0.5 * self.height * DOWN,
|
||||
)
|
||||
subdivs.add(subdiv)
|
||||
subdivs.move_to(self.get_center())
|
||||
return subdivs
|
||||
|
||||
|
||||
def get_sequence_subdivs_for_level(self,r):
|
||||
subdivs = VGroup()
|
||||
x = - 0.5 * self.width
|
||||
dx = 1.0 / 2**r
|
||||
for k in range(1, 2 ** r):
|
||||
proportion = dx
|
||||
x += proportion * self.width
|
||||
subdiv = DashedLine(
|
||||
x * RIGHT + 0.5 * self.height * UP,
|
||||
x * RIGHT + 0.5 * self.height * DOWN,
|
||||
)
|
||||
subdivs.add(subdiv)
|
||||
subdivs.move_to(self.get_center())
|
||||
return subdivs
|
||||
|
||||
|
||||
def get_outcome_centers_for_level(self,r):
|
||||
|
||||
dpos = float(self.width) / (2 ** r) * RIGHT
|
||||
pos = 0.5 * self.width * LEFT + 0.5 * dpos
|
||||
centers = []
|
||||
for k in range(0, 2 ** r):
|
||||
centers.append(self.get_center() + pos + k * dpos)
|
||||
|
||||
return centers
|
||||
|
||||
def get_outcome_rects_for_level(self, r, inset = False, with_labels = False):
|
||||
|
||||
centers = self.get_outcome_centers_for_level(r)
|
||||
if inset == True:
|
||||
outcome_width = self.outcome_shrinkage_factor_x * float(self.width) / (2 ** r)
|
||||
outcome_height = self.outcome_shrinkage_factor_y * self.height
|
||||
else:
|
||||
outcome_width = float(self.width) / (2 ** r)
|
||||
outcome_height = self.height
|
||||
|
||||
corner_radius = 0.1 # max(0.1, 0.3 * min(outcome_width, outcome_height))
|
||||
# this scales down the corner radius for very narrow rects
|
||||
rect = RoundedRectangle(
|
||||
width = outcome_width,
|
||||
height = outcome_height,
|
||||
corner_radius = corner_radius,
|
||||
fill_color = OUTCOME_COLOR,
|
||||
fill_opacity = OUTCOME_OPACITY,
|
||||
stroke_width = 0
|
||||
)
|
||||
rects = VGroup()
|
||||
for center in centers:
|
||||
rects.add(rect.copy().move_to(center))
|
||||
|
||||
rects.move_to(self.get_center())
|
||||
|
||||
|
||||
if with_labels == False:
|
||||
return rects
|
||||
|
||||
# else
|
||||
sequences = self.get_coin_sequences_for_level(r)
|
||||
labels = VGroup()
|
||||
for (seq, rect) in zip(sequences, rects):
|
||||
coin_seq = CoinSequence(seq, direction = DOWN)
|
||||
coin_seq.shift(rect.get_center() - coin_seq.get_center())
|
||||
# not simply move_to bc coin_seq is not centered
|
||||
rect.add(coin_seq)
|
||||
rect.label = coin_seq
|
||||
|
||||
return rects
|
||||
|
||||
def get_coin_sequences_for_level(self,r):
|
||||
# array of arrays of characters
|
||||
if r < 0 or int(r) != r:
|
||||
raise Exception("Level must be a positive integer")
|
||||
if r == 0:
|
||||
return []
|
||||
if r == 1:
|
||||
return [["H"], ["T"]]
|
||||
|
||||
previous_seq_array = self.get_coin_sequences_for_level(r - 1)
|
||||
subdiv_lengths = [choose(r - 1, k) for k in range(r)]
|
||||
|
||||
seq_array = []
|
||||
index = 0
|
||||
for length in subdiv_lengths:
|
||||
|
||||
for seq in previous_seq_array[index:index + length]:
|
||||
seq_copy = copy.copy(seq)
|
||||
seq_copy.append("H")
|
||||
seq_array.append(seq_copy)
|
||||
|
||||
for seq in previous_seq_array[index:index + length]:
|
||||
seq_copy = copy.copy(seq)
|
||||
seq_copy.append("T")
|
||||
seq_array.append(seq_copy)
|
||||
index += length
|
||||
|
||||
return seq_array
|
||||
|
||||
|
||||
def get_outcome_width_for_level(self,r):
|
||||
return self.width / (2**r)
|
||||
|
||||
def get_rect_widths_for_level(self, r):
|
||||
ret_arr = []
|
||||
for k in range(0, r):
|
||||
proportion = float(choose(r,k)) / 2**r
|
||||
ret_arr.append(proportion * self.width)
|
||||
return ret_arr
|
||||
|
||||
|
||||
|
||||
|
||||
class SplitRectsInBrickWall(AnimationGroup):
|
||||
|
||||
def __init__(self, mobject, **kwargs):
|
||||
|
||||
#print mobject.height, mobject.get_height()
|
||||
r = self.subdiv_level = mobject.subdiv_level + 1
|
||||
|
||||
subdivs = VGroup()
|
||||
x = -0.5 * mobject.get_width()
|
||||
|
||||
anims = []
|
||||
for k in range(0, r):
|
||||
proportion = float(choose(r,k)) / 2**r
|
||||
x += proportion * mobject.get_width()
|
||||
subdiv = DashedLine(
|
||||
mobject.get_top() + x * RIGHT,
|
||||
mobject.get_bottom() + x * RIGHT,
|
||||
dash_length = 0.05
|
||||
)
|
||||
subdivs.add(subdiv)
|
||||
anims.append(ShowCreation(subdiv))
|
||||
|
||||
mobject.add(subdivs)
|
||||
AnimationGroup.__init__(self, *anims, **kwargs)
|
||||
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
from mobject.geometry import *
|
||||
from active_projects.eop.reusables.eop_helpers import *
|
||||
from active_projects.eop.reusables.eop_constants import *
|
||||
|
||||
class CoinFlipTree(VGroup):
|
||||
CONFIG = {
|
||||
"total_width": 12,
|
||||
"level_height": 0.8,
|
||||
"nb_levels": 4,
|
||||
"sort_until_level": 3
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
VGroup.__init__(self, **kwargs)
|
||||
|
||||
self.rows = []
|
||||
for n in range(self.nb_levels + 1):
|
||||
if n <= self.sort_until_level:
|
||||
self.create_row(n, sorted = True)
|
||||
else:
|
||||
self.create_row(n, sorted = False)
|
||||
|
||||
|
||||
for row in self.rows:
|
||||
for leaf in row:
|
||||
dot = Dot()
|
||||
dot.move_to(leaf[0])
|
||||
line = Line(leaf[2], leaf[0])
|
||||
if leaf[2][0] > leaf[0][0]:
|
||||
line_color = COLOR_HEADS
|
||||
else:
|
||||
line_color = COLOR_TAILS
|
||||
line.set_stroke(color = line_color)
|
||||
group = VGroup()
|
||||
group.add(dot)
|
||||
group.add_to_back(line)
|
||||
self.add(group)
|
||||
|
||||
|
||||
|
||||
|
||||
def create_row(self, level, sorted = True):
|
||||
|
||||
if level == 0:
|
||||
new_row = [[ORIGIN,0,ORIGIN]] # is its own parent
|
||||
self.rows.append(new_row)
|
||||
return
|
||||
|
||||
previous_row = self.rows[level - 1]
|
||||
new_row = []
|
||||
dx = float(self.total_width) / (2 ** level)
|
||||
x = - 0.5 * self.total_width + 0.5 * dx
|
||||
y = - self.level_height * level
|
||||
for root in previous_row:
|
||||
root_point = root[0]
|
||||
root_tally = root[1]
|
||||
for i in range(2): # 0 = heads = left, 1 = tails = right
|
||||
leaf = x * RIGHT + y * UP
|
||||
new_row.append([leaf, root_tally + i, root_point]) # leaf and its parent
|
||||
x += dx
|
||||
|
||||
if sorted:
|
||||
# sort the new_row by its tallies
|
||||
sorted_row = []
|
||||
x = - 0.5 * self.total_width + 0.5 * dx
|
||||
for i in range(level + 1):
|
||||
for leaf in new_row:
|
||||
if leaf[1] == i:
|
||||
sorted_leaf = leaf
|
||||
sorted_leaf[0][0] = x
|
||||
x += dx
|
||||
sorted_row.append(leaf)
|
||||
self.rows.append(sorted_row)
|
||||
else:
|
||||
self.rows.append(new_row)
|
||||
@@ -1,112 +0,0 @@
|
||||
from mobject.types.vectorized_mobject import *
|
||||
from animation.animation import *
|
||||
from animation.composition import *
|
||||
from mobject.geometry import Rectangle, Line
|
||||
from utils.rate_functions import *
|
||||
from for_3b1b_videos.pi_creature_scene import *
|
||||
from active_projects.eop.reusables.eop_helpers import *
|
||||
from active_projects.eop.reusables.eop_constants import *
|
||||
from active_projects.eop.reusables.coin_flipping_pi_creature import *
|
||||
|
||||
|
||||
class PiCreatureCoin(VMobject):
|
||||
CONFIG = {
|
||||
"diameter": 0.8,
|
||||
"thickness": 0.2,
|
||||
"nb_ridges" : 7,
|
||||
"stroke_color": YELLOW,
|
||||
"stroke_width": 3,
|
||||
"fill_color": YELLOW,
|
||||
"fill_opacity": 0.7,
|
||||
}
|
||||
|
||||
def generate_points(self):
|
||||
outer_rect = Rectangle(
|
||||
width = self.diameter,
|
||||
height = self.thickness,
|
||||
fill_color = self.fill_color,
|
||||
fill_opacity = self.fill_opacity,
|
||||
stroke_color = self.stroke_color,
|
||||
stroke_width = 0, #self.stroke_width
|
||||
)
|
||||
self.add(outer_rect)
|
||||
PI = TAU/2
|
||||
ridge_angles = np.arange(PI/self.nb_ridges,PI,PI/self.nb_ridges)
|
||||
ridge_positions = 0.5 * self.diameter * np.array([
|
||||
np.cos(theta) for theta in ridge_angles
|
||||
])
|
||||
ridge_color = interpolate_color(BLACK,self.stroke_color,0.5)
|
||||
for x in ridge_positions:
|
||||
ridge = Line(
|
||||
x * RIGHT + 0.5 * self.thickness * DOWN,
|
||||
x * RIGHT + 0.5 * self.thickness * UP,
|
||||
stroke_color = ridge_color,
|
||||
stroke_width = self.stroke_width
|
||||
)
|
||||
self.add(ridge)
|
||||
|
||||
class CoinFlippingPiCreature(PiCreature):
|
||||
CONFIG = {
|
||||
"flip_height": 3
|
||||
}
|
||||
|
||||
def __init__(self, mode = "coin_flip_1", **kwargs):
|
||||
|
||||
coin = PiCreatureCoin()
|
||||
PiCreature.__init__(self, mode = mode, **kwargs)
|
||||
self.coin = coin
|
||||
self.add(coin)
|
||||
right_arm = self.get_arm_copies()[1]
|
||||
coin.rotate(-TAU/24)
|
||||
coin.next_to(right_arm, RIGHT+UP, buff = 0)
|
||||
coin.shift(0.1 * self.get_width() * LEFT)
|
||||
coin.shift(0.2 * DOWN)
|
||||
|
||||
def flip_coin_up(self):
|
||||
self.change("coin_flip_2")
|
||||
|
||||
|
||||
|
||||
class FlipUpAndDown(Animation):
|
||||
CONFIG = {
|
||||
"vector" : UP,
|
||||
"height" : 3,
|
||||
"nb_turns" : 1
|
||||
}
|
||||
|
||||
def update(self,t):
|
||||
self.mobject.shift(self.height * 4 * t * (1 - t) * self.vector)
|
||||
self.mobject.rotate(t * self.nb_turns * TAU)
|
||||
|
||||
class FlipCoin(AnimationGroup):
|
||||
CONFIG = {
|
||||
"coin_rate_func" : there_and_back,
|
||||
"pi_rate_func" : lambda t : there_and_back_with_pause(t, 1./4)
|
||||
}
|
||||
def __init__(self, pi_creature, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
pi_creature_motion = ApplyMethod(
|
||||
pi_creature.flip_coin_up,
|
||||
rate_func = self.pi_rate_func,
|
||||
**kwargs
|
||||
)
|
||||
coin_motion = Succession(
|
||||
EmptyAnimation(run_time = 1.0),
|
||||
FlipUpAndDown(
|
||||
pi_creature.coin,
|
||||
vector = UP,
|
||||
nb_turns = 5,
|
||||
height = pi_creature.flip_height * pi_creature.get_height(),
|
||||
rate_func = self.coin_rate_func,
|
||||
**kwargs
|
||||
)
|
||||
)
|
||||
AnimationGroup.__init__(self,pi_creature_motion, coin_motion)
|
||||
|
||||
class CoinFlippingPiCreatureScene(Scene):
|
||||
|
||||
def construct(self):
|
||||
|
||||
randy = CoinFlippingPiCreature(color = MAROON_E)
|
||||
self.add(randy)
|
||||
self.play(FlipCoin(randy, run_time = 3))
|
||||
@@ -1,111 +0,0 @@
|
||||
from mobject.geometry import *
|
||||
from mobject.svg.tex_mobject import *
|
||||
from active_projects.eop.reusables.upright_coins import *
|
||||
|
||||
|
||||
class CoinStack(VGroup):
|
||||
CONFIG = {
|
||||
"coin_thickness": COIN_THICKNESS,
|
||||
"size": 5,
|
||||
"face": FlatCoin,
|
||||
}
|
||||
|
||||
def generate_points(self):
|
||||
for n in range(self.size):
|
||||
coin = self.face(thickness = self.coin_thickness)
|
||||
coin.shift(n * self.coin_thickness * UP)
|
||||
self.add(coin)
|
||||
if self.size == 0:
|
||||
point = VectorizedPoint()
|
||||
self.add(point)
|
||||
|
||||
class HeadsStack(CoinStack):
|
||||
CONFIG = {
|
||||
"face": FlatHeads
|
||||
}
|
||||
|
||||
class TailsStack(CoinStack):
|
||||
CONFIG = {
|
||||
"face": FlatTails
|
||||
}
|
||||
|
||||
|
||||
|
||||
class DecimalTally(TextMobject):
|
||||
|
||||
def __init__(self, heads, tails, **kwargs):
|
||||
|
||||
TextMobject.__init__(self, str(heads), "\\textemdash\,", str(tails), **kwargs)
|
||||
self[0].set_color(COLOR_HEADS)
|
||||
self[-1].set_color(COLOR_TAILS)
|
||||
# this only works for single-digit tallies
|
||||
|
||||
|
||||
|
||||
|
||||
class TallyStack(VGroup):
|
||||
CONFIG = {
|
||||
"coin_thickness": COIN_THICKNESS,
|
||||
"show_decimals": True
|
||||
}
|
||||
|
||||
def __init__(self, h, t, anchor = ORIGIN, **kwargs):
|
||||
self.nb_heads = h
|
||||
self.nb_tails = t
|
||||
self.anchor = anchor
|
||||
VGroup.__init__(self,**kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
stack1 = HeadsStack(size = self.nb_heads, coin_thickness = self.coin_thickness)
|
||||
stack2 = TailsStack(size = self.nb_tails, coin_thickness = self.coin_thickness)
|
||||
stack1.next_to(self.anchor, LEFT, buff = 0.5 * SMALL_BUFF)
|
||||
stack2.next_to(self.anchor, RIGHT, buff = 0.5 * SMALL_BUFF)
|
||||
stack1.align_to(self.anchor, DOWN)
|
||||
stack2.align_to(self.anchor, DOWN)
|
||||
self.heads_stack = stack1
|
||||
self.tails_stack = stack2
|
||||
self.add(stack1, stack2)
|
||||
self.background_rect = background_rect = RoundedRectangle(
|
||||
width = TALLY_BACKGROUND_WIDTH,
|
||||
height = TALLY_BACKGROUND_WIDTH,
|
||||
corner_radius = 0.1,
|
||||
fill_color = TALLY_BACKGROUND_COLOR,
|
||||
fill_opacity = 1.0,
|
||||
stroke_width = 3
|
||||
).align_to(self.anchor, DOWN).shift(0.1 * DOWN)
|
||||
self.add_to_back(background_rect)
|
||||
|
||||
self.decimal_tally = DecimalTally(self.nb_heads, self.nb_tails)
|
||||
self.position_decimal_tally(self.decimal_tally)
|
||||
if self.show_decimals:
|
||||
self.add(self.decimal_tally)
|
||||
|
||||
def position_decimal_tally(self, decimal_tally):
|
||||
decimal_tally.match_width(self.background_rect)
|
||||
decimal_tally.scale(0.6)
|
||||
decimal_tally.next_to(self.background_rect.get_top(), DOWN, buff = 0.15)
|
||||
return decimal_tally
|
||||
|
||||
|
||||
def move_anchor_to(self, new_anchor):
|
||||
for submob in self.submobjects:
|
||||
submob.shift(new_anchor - self.anchor)
|
||||
|
||||
self.anchor = new_anchor
|
||||
self.position_decimal_tally(self.decimal_tally)
|
||||
|
||||
return self
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
from mobject.svg.svg_mobject import *
|
||||
from mobject.geometry import *
|
||||
from mobject.numbers import *
|
||||
|
||||
class DieFace(SVGMobject):
|
||||
|
||||
def __init__(self, value, **kwargs):
|
||||
|
||||
self.value = value
|
||||
self.file_name = "Dice-" + str(value)
|
||||
self.ensure_valid_file()
|
||||
SVGMobject.__init__(self, file_name = self.file_name)
|
||||
|
||||
class RowOfDice(VGroup):
|
||||
CONFIG = {
|
||||
"values" : list(range(1,7)),
|
||||
"direction": RIGHT,
|
||||
}
|
||||
|
||||
def generate_points(self):
|
||||
for value in self.values:
|
||||
new_die = DieFace(value)
|
||||
new_die.submobjects[0].set_fill(opacity = 0)
|
||||
new_die.submobjects[0].set_stroke(width = 7)
|
||||
new_die.next_to(self, self.direction)
|
||||
self.add(new_die)
|
||||
self.move_to(ORIGIN)
|
||||
|
||||
|
||||
class TwoDiceTable(VMobject):
|
||||
CONFIG = {
|
||||
"cell_size" : 1,
|
||||
"label_scale": 0.7
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
VMobject.__init__(self, **kwargs)
|
||||
colors = color_gradient([RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE], 13)
|
||||
|
||||
self.cells = VGroup()
|
||||
self.labels = VGroup()
|
||||
for i in range(1,7):
|
||||
for j in range(1,7):
|
||||
cell = Square(side_length = self.cell_size)
|
||||
cell.set_fill(color = colors[i+j], opacity = 0.8)
|
||||
cell.move_to(i*self.cell_size*DOWN + j*self.cell_size*RIGHT)
|
||||
self.cells.add(cell)
|
||||
label = Integer(i+j).scale(self.label_scale)
|
||||
label.move_to(cell)
|
||||
self.labels.add(label)
|
||||
|
||||
|
||||
self.add(self.cells, self.labels)
|
||||
row1 = RowOfDice().match_width(self)
|
||||
row2 = row1.copy().rotate(-TAU/4)
|
||||
row1.next_to(self, UP)
|
||||
row2.next_to(self, LEFT)
|
||||
self.rows = VGroup(row1, row2)
|
||||
self.add(self.rows)
|
||||
self.center()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
from constants import *
|
||||
|
||||
COIN_RADIUS = 0.18
|
||||
COIN_THICKNESS = 0.4 * COIN_RADIUS
|
||||
COIN_FORESHORTENING = 0.5
|
||||
COIN_NB_RIDGES = 20
|
||||
COIN_STROKE_WIDTH = 2
|
||||
|
||||
COIN_SEQUENCE_SPACING = 0.1
|
||||
|
||||
GRADE_COLOR_1 = COLOR_HEADS = RED_E
|
||||
GRADE_COLOR_2 = COLOR_TAILS = BLUE_C
|
||||
|
||||
COLOR_HEADS_COIN = RED
|
||||
COLOR_TAILS_COIN = BLUE_E
|
||||
|
||||
TALLY_BACKGROUND_WIDTH = 1.0
|
||||
TALLY_BACKGROUND_COLOR = BLACK
|
||||
|
||||
SICKLY_GREEN = "#9BBD37"
|
||||
|
||||
OUTCOME_COLOR = WHITE
|
||||
OUTCOME_OPACITY = 0.5
|
||||
@@ -1,36 +0,0 @@
|
||||
from utils.color import *
|
||||
from active_projects.eop.reusables.eop_constants import *
|
||||
|
||||
def binary(i):
|
||||
# returns an array of 0s and 1s
|
||||
if i == 0:
|
||||
return []
|
||||
j = i
|
||||
binary_array = []
|
||||
while j > 0:
|
||||
jj = j/2
|
||||
if jj > 0:
|
||||
binary_array.append(j % 2)
|
||||
else:
|
||||
binary_array.append(1)
|
||||
j = jj
|
||||
return binary_array[::-1]
|
||||
|
||||
def nb_of_ones(i):
|
||||
return binary(i).count(1)
|
||||
|
||||
|
||||
def rainbow_color(alpha):
|
||||
nb_colors = 100
|
||||
rainbow = color_gradient([RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE], nb_colors)
|
||||
rainbow = np.append(rainbow,PURPLE)
|
||||
index = int(alpha * nb_colors)
|
||||
return rainbow[index]
|
||||
|
||||
def graded_color(n,k):
|
||||
if n != 0:
|
||||
alpha = float(k)/n
|
||||
else:
|
||||
alpha = 0.5
|
||||
color = interpolate_color(GRADE_COLOR_1, GRADE_COLOR_2, alpha)
|
||||
return color
|
||||
@@ -1,348 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from random import *
|
||||
|
||||
def text_range(start,stop,step): # a range as a list of strings
|
||||
numbers = np.arange(start,stop,step)
|
||||
labels = []
|
||||
for x in numbers:
|
||||
labels.append(str(x))
|
||||
return labels
|
||||
|
||||
|
||||
class Histogram(VMobject):
|
||||
|
||||
CONFIG = {
|
||||
"start_color" : RED,
|
||||
"end_color" : BLUE,
|
||||
"x_scale" : 1.0,
|
||||
"y_scale" : 1.0,
|
||||
"x_labels" : "auto", # widths, mids, auto, none, [...]
|
||||
"y_labels" : "auto", # auto, none, [...]
|
||||
"y_label_position" : "top", # "center"
|
||||
"x_min" : 0,
|
||||
"bar_stroke_width" : 5,
|
||||
"outline_stroke_width" : 0,
|
||||
"stroke_color" : WHITE
|
||||
}
|
||||
|
||||
def __init__(self, x_values, y_values, mode = "widths", **kwargs):
|
||||
# mode = "widths" : x_values means the widths of the bars
|
||||
# mode = "posts" : x_values means the delimiters btw the bars
|
||||
|
||||
digest_config(self, kwargs)
|
||||
|
||||
if mode == "widths" and len(x_values) != len(y_values):
|
||||
raise Exception("Array lengths do not match up!")
|
||||
elif mode == "posts" and len(x_values) != len(y_values) + 1:
|
||||
raise Exception("Array lengths do not match up!")
|
||||
|
||||
|
||||
self.y_values = y_values
|
||||
self.x_values = x_values
|
||||
self.mode = mode
|
||||
self.process_values()
|
||||
|
||||
VMobject.__init__(self, **kwargs)
|
||||
|
||||
|
||||
def process_values(self):
|
||||
|
||||
# preliminaries
|
||||
self.y_values = np.array(self.y_values)
|
||||
|
||||
if self.mode == "widths":
|
||||
self.widths = self.x_values
|
||||
self.posts = np.cumsum(self.widths)
|
||||
self.posts = np.insert(self.posts, 0, 0)
|
||||
self.posts += self.x_min
|
||||
self.x_max = self.posts[-1]
|
||||
elif self.mode == "posts":
|
||||
self.posts = self.x_values
|
||||
self.widths = self.x_values[1:] - self.x_values[:-1]
|
||||
self.x_min = self.posts[0]
|
||||
self.x_max = self.posts[-1]
|
||||
else:
|
||||
raise Exception("Invalid mode or no mode specified!")
|
||||
|
||||
self.x_mids = 0.5 * (self.posts[:-1] + self.posts[1:])
|
||||
|
||||
self.widths_scaled = self.x_scale * self.widths
|
||||
self.posts_scaled = self.x_scale * self.posts
|
||||
self.x_min_scaled = self.x_scale * self.x_min
|
||||
self.x_max_scaled = self.x_scale * self.x_max
|
||||
|
||||
self.y_values_scaled = self.y_scale * self.y_values
|
||||
|
||||
|
||||
|
||||
def generate_points(self):
|
||||
|
||||
self.process_values()
|
||||
for submob in self.submobjects:
|
||||
self.remove(submob)
|
||||
|
||||
def empty_string_array(n):
|
||||
arr = []
|
||||
for i in range(n):
|
||||
arr.append("")
|
||||
return arr
|
||||
|
||||
def num_arr_to_string_arr(arr): # converts number array to string array
|
||||
ret_arr = []
|
||||
for x in arr:
|
||||
if x == np.floor(x):
|
||||
new_x = int(np.floor(x))
|
||||
else:
|
||||
new_x = x
|
||||
ret_arr.append(str(new_x))
|
||||
return ret_arr
|
||||
|
||||
previous_bar = ORIGIN
|
||||
self.bars = VGroup()
|
||||
self.x_labels_group = VGroup()
|
||||
self.y_labels_group = VGroup()
|
||||
outline_points = []
|
||||
|
||||
if self.x_labels == "widths":
|
||||
self.x_labels = num_arr_to_string_arr(self.widths)
|
||||
elif self.x_labels == "mids":
|
||||
self.x_labels = num_arr_to_string_arr(self.x_mids)
|
||||
elif self.x_labels == "auto":
|
||||
self.x_labels = num_arr_to_string_arr(self.x_mids)
|
||||
elif self.x_labels == "none":
|
||||
self.x_labels = empty_string_array(len(self.widths))
|
||||
|
||||
if self.y_labels == "auto":
|
||||
self.y_labels = num_arr_to_string_arr(self.y_values)
|
||||
elif self.y_labels == "none":
|
||||
self.y_labels = empty_string_array(len(self.y_values))
|
||||
|
||||
|
||||
|
||||
|
||||
for (i,x) in enumerate(self.x_mids):
|
||||
|
||||
bar = Rectangle(
|
||||
width = self.widths_scaled[i],
|
||||
height = self.y_values_scaled[i],
|
||||
stroke_width = self.bar_stroke_width,
|
||||
stroke_color = self.stroke_color,
|
||||
)
|
||||
if bar.height == 0:
|
||||
bar.height = 0.01
|
||||
bar.generate_points()
|
||||
|
||||
t = float(x - self.x_min)/(self.x_max - self.x_min)
|
||||
bar_color = interpolate_color(
|
||||
self.start_color,
|
||||
self.end_color,
|
||||
t
|
||||
)
|
||||
bar.set_fill(color = bar_color, opacity = 1)
|
||||
bar.next_to(previous_bar,RIGHT,buff = 0, aligned_edge = DOWN)
|
||||
|
||||
self.bars.add(bar)
|
||||
|
||||
x_label = TextMobject(self.x_labels[i])
|
||||
x_label.next_to(bar,DOWN)
|
||||
self.x_labels_group.add(x_label)
|
||||
|
||||
y_label = TextMobject(self.y_labels[i])
|
||||
if self.y_label_position == "top":
|
||||
y_label.next_to(bar, UP)
|
||||
elif self.y_label_position == "center":
|
||||
y_label.move_to(bar)
|
||||
else:
|
||||
raise Exception("y_label_position must be top or center")
|
||||
self.y_labels_group.add(y_label)
|
||||
|
||||
if i == 0:
|
||||
# start with the lower left
|
||||
outline_points.append(bar.get_anchors()[-2])
|
||||
|
||||
# upper two points of each bar
|
||||
outline_points.append(bar.get_anchors()[0])
|
||||
outline_points.append(bar.get_anchors()[1])
|
||||
|
||||
previous_bar = bar
|
||||
# close the outline
|
||||
# lower right
|
||||
outline_points.append(bar.get_anchors()[2])
|
||||
# lower left
|
||||
outline_points.append(outline_points[0])
|
||||
|
||||
self.outline = Polygon(*outline_points,
|
||||
stroke_width = self.outline_stroke_width,
|
||||
stroke_color = self.stroke_color)
|
||||
self.add(self.bars, self.x_labels_group, self.y_labels_group, self.outline)
|
||||
|
||||
self.move_to(ORIGIN)
|
||||
|
||||
def get_lower_left_point(self):
|
||||
return self.bars[0].get_anchors()[-2]
|
||||
|
||||
|
||||
|
||||
class BuildUpHistogram(Animation):
|
||||
|
||||
def __init__(self, hist, **kwargs):
|
||||
self.histogram = hist
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class FlashThroughHistogram(Animation):
|
||||
CONFIG = {
|
||||
"cell_color" : WHITE,
|
||||
"cell_opacity" : 0.8,
|
||||
"hist_opacity" : 0.2
|
||||
}
|
||||
|
||||
def __init__(self, mobject,
|
||||
direction = "horizontal",
|
||||
mode = "random",
|
||||
**kwargs):
|
||||
|
||||
digest_config(self, kwargs)
|
||||
|
||||
self.cell_height = mobject.y_scale
|
||||
self.prototype_cell = Rectangle(
|
||||
width = 1,
|
||||
height = self.cell_height,
|
||||
fill_color = self.cell_color,
|
||||
fill_opacity = self.cell_opacity,
|
||||
stroke_width = 0,
|
||||
)
|
||||
|
||||
x_values = mobject.x_values
|
||||
y_values = mobject.y_values
|
||||
|
||||
self.mode = mode
|
||||
self.direction = direction
|
||||
|
||||
self.generate_cell_indices(x_values,y_values)
|
||||
Animation.__init__(self,mobject,**kwargs)
|
||||
|
||||
|
||||
|
||||
def generate_cell_indices(self,x_values,y_values):
|
||||
|
||||
self.cell_indices = []
|
||||
for (i,x) in enumerate(x_values):
|
||||
|
||||
nb_cells = int(np.floor(y_values[i]))
|
||||
for j in range(nb_cells):
|
||||
self.cell_indices.append((i, j))
|
||||
|
||||
self.reordered_cell_indices = self.cell_indices
|
||||
if self.mode == "random":
|
||||
shuffle(self.reordered_cell_indices)
|
||||
|
||||
|
||||
def cell_for_index(self,i,j):
|
||||
|
||||
if self.direction == "vertical":
|
||||
width = self.mobject.x_scale
|
||||
height = self.mobject.y_scale
|
||||
x = (i + 0.5) * self.mobject.x_scale
|
||||
y = (j + 0.5) * self.mobject.y_scale
|
||||
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
||||
|
||||
elif self.direction == "horizontal":
|
||||
width = self.mobject.x_scale / self.mobject.y_values[i]
|
||||
height = self.mobject.y_scale * self.mobject.y_values[i]
|
||||
x = i * self.mobject.x_scale + (j + 0.5) * width
|
||||
y = height / 2
|
||||
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
||||
|
||||
cell = Rectangle(width = width, height = height)
|
||||
cell.move_to(center)
|
||||
return cell
|
||||
|
||||
|
||||
def interpolate_mobject(self,t):
|
||||
|
||||
if t == 0:
|
||||
self.mobject.add(self.prototype_cell)
|
||||
|
||||
flash_nb = int(t * (len(self.cell_indices))) - 1
|
||||
(i,j) = self.reordered_cell_indices[flash_nb]
|
||||
cell = self.cell_for_index(i,j)
|
||||
self.prototype_cell.width = cell.get_width()
|
||||
self.prototype_cell.height = cell.get_height()
|
||||
self.prototype_cell.generate_points()
|
||||
self.prototype_cell.move_to(cell.get_center())
|
||||
|
||||
if t == 1:
|
||||
self.mobject.remove(self.prototype_cell)
|
||||
|
||||
|
||||
def clean_up_from_scene(self, scene = None):
|
||||
Animation.clean_up_from_scene(self, scene)
|
||||
self.update(1)
|
||||
if scene is not None:
|
||||
if self.is_remover():
|
||||
scene.remove(self.prototype_cell)
|
||||
else:
|
||||
scene.add(self.prototype_cell)
|
||||
return self
|
||||
|
||||
|
||||
|
||||
class OutlineableBars(VGroup):
|
||||
|
||||
# A group of bars (rectangles), together with
|
||||
# a method that draws an outline around them,
|
||||
# assuming the bars are arranged in a histogram
|
||||
# (aligned at the bottom without gaps).
|
||||
|
||||
# We use this to morph a row of bricks into a histogram.
|
||||
|
||||
CONFIG = {
|
||||
"outline_stroke_width" : 3,
|
||||
"stroke_color" : WHITE
|
||||
}
|
||||
def create_outline(self, animated = False, **kwargs):
|
||||
|
||||
outline_points = []
|
||||
|
||||
for (i, bar) in enumerate(self.submobjects):
|
||||
|
||||
if i == 0:
|
||||
# start with the lower left
|
||||
outline_points.append(bar.get_corner(DOWN + LEFT))
|
||||
|
||||
# upper two points of each bar
|
||||
outline_points.append(bar.get_corner(UP + LEFT))
|
||||
outline_points.append(bar.get_corner(UP + RIGHT))
|
||||
|
||||
previous_bar = bar
|
||||
# close the outline
|
||||
# lower right
|
||||
outline_points.append(previous_bar.get_corner(DOWN + RIGHT))
|
||||
# lower left
|
||||
outline_points.append(outline_points[0])
|
||||
|
||||
self.outline = Polygon(*outline_points,
|
||||
stroke_width = self.outline_stroke_width,
|
||||
stroke_color = self.stroke_color)
|
||||
|
||||
if animated:
|
||||
self.play(FadeIn(self.outline, **kwargs))
|
||||
return self.outline
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
from for_3b1b_videos.pi_creature import *
|
||||
from active_projects.eop.reusables.eop_constants import *
|
||||
|
||||
|
||||
class SicklyPiCreature(PiCreature):
|
||||
CONFIG = {
|
||||
"sick_color": SICKLY_GREEN
|
||||
}
|
||||
|
||||
def get_slightly_sick(self):
|
||||
|
||||
self.save_state()
|
||||
self.set_color(self.sick_color)
|
||||
|
||||
def get_sick(self):
|
||||
|
||||
self.get_slightly_sick()
|
||||
self.change_mode("sick")
|
||||
|
||||
def get_better(self):
|
||||
self.restore()
|
||||
@@ -1,132 +0,0 @@
|
||||
|
||||
from mobject.geometry import *
|
||||
from mobject.svg.tex_mobject import *
|
||||
from utils.color import *
|
||||
|
||||
from active_projects.eop.reusables.eop_helpers import *
|
||||
from active_projects.eop.reusables.eop_constants import *
|
||||
|
||||
class UprightCoin(Circle):
|
||||
# For use in coin sequences
|
||||
CONFIG = {
|
||||
"radius": COIN_RADIUS,
|
||||
"stroke_width": COIN_STROKE_WIDTH,
|
||||
"stroke_color": WHITE,
|
||||
"fill_opacity": 1,
|
||||
"symbol": "\euro"
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
Circle.__init__(self,**kwargs)
|
||||
self.symbol_mob = TextMobject(self.symbol, stroke_color = self.stroke_color)
|
||||
self.symbol_mob.set_height(0.5*self.get_height()).move_to(self)
|
||||
self.add(self.symbol_mob)
|
||||
|
||||
class UprightHeads(UprightCoin):
|
||||
CONFIG = {
|
||||
"fill_color": COLOR_HEADS_COIN,
|
||||
"symbol": "H",
|
||||
}
|
||||
|
||||
class UprightTails(UprightCoin):
|
||||
CONFIG = {
|
||||
"fill_color": COLOR_TAILS_COIN,
|
||||
"symbol": "T",
|
||||
}
|
||||
|
||||
class CoinSequence(VGroup):
|
||||
CONFIG = {
|
||||
"sequence": [],
|
||||
"radius" : COIN_RADIUS,
|
||||
"spacing": COIN_SEQUENCE_SPACING,
|
||||
"direction": RIGHT
|
||||
}
|
||||
|
||||
def __init__(self, sequence, **kwargs):
|
||||
VGroup.__init__(self, **kwargs)
|
||||
self.sequence = sequence
|
||||
offset = 0
|
||||
for symbol in self.sequence:
|
||||
if symbol == "H":
|
||||
new_coin = UprightHeads(radius = self.radius)
|
||||
elif symbol == "T":
|
||||
new_coin = UprightTails(radius = self.radius)
|
||||
else:
|
||||
new_coin = UprightCoin(symbol = symbol, radius = self.radius)
|
||||
new_coin.shift(offset * self.direction)
|
||||
self.add(new_coin)
|
||||
offset += self.spacing
|
||||
|
||||
|
||||
class FlatCoin(UprightCoin):
|
||||
# For use in coin stacks
|
||||
CONFIG = {
|
||||
"thickness": COIN_THICKNESS,
|
||||
"foreshortening": COIN_FORESHORTENING,
|
||||
"nb_ridges": COIN_NB_RIDGES
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
UprightCoin.__init__(self, **kwargs)
|
||||
self.symbol_mob.rotate(TAU/8)
|
||||
self.stretch_in_place(self.foreshortening, 1)
|
||||
|
||||
# draw the edge
|
||||
control_points1 = self.points[12:25].tolist()
|
||||
control_points2 = self.copy().shift(self.thickness * DOWN).points[12:25].tolist()
|
||||
edge_anchors_and_handles = control_points1
|
||||
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * DOWN)
|
||||
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * UP)
|
||||
edge_anchors_and_handles += control_points2[::-1] # list concatenation
|
||||
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * UP)
|
||||
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * DOWN)
|
||||
edge_anchors_and_handles.append(control_points1[0])
|
||||
#edge_anchors_and_handles = edge_anchors_and_handles[::-1]
|
||||
edge = VMobject()
|
||||
edge.set_points(edge_anchors_and_handles)
|
||||
edge.set_fill(
|
||||
color = self.fill_color,
|
||||
opacity = self.fill_opacity
|
||||
)
|
||||
edge.set_stroke(width = self.stroke_width)
|
||||
self.add(edge)
|
||||
|
||||
# draw the ridges
|
||||
PI = TAU/2
|
||||
dtheta = PI/self.nb_ridges
|
||||
ridge_angles = np.arange(dtheta,PI,dtheta)
|
||||
# add a twist onto each coin
|
||||
ridge_angles += np.random.rand(1) * dtheta
|
||||
# crop the angles that overshoot on either side
|
||||
ridge_angles = ridge_angles[(ridge_angles > 0) * (ridge_angles < PI)]
|
||||
ridge_positions = 0.5 * 2 * self.radius * np.array([
|
||||
np.cos(theta) for theta in ridge_angles
|
||||
])
|
||||
ridge_color = interpolate_color(self.stroke_color, self.fill_color, 0.7)
|
||||
for x in ridge_positions:
|
||||
y = -(1 - (x/self.radius)**2)**0.5 * self.foreshortening * self.radius
|
||||
ridge = Line(
|
||||
x * RIGHT + y * UP,
|
||||
x * RIGHT + y * UP + self.thickness * DOWN,
|
||||
stroke_color = ridge_color,
|
||||
stroke_width = self.stroke_width
|
||||
)
|
||||
self.add(ridge)
|
||||
|
||||
# redraw the unfilled edge to cover the ridge ends
|
||||
empty_edge = edge.copy()
|
||||
empty_edge.set_fill(opacity = 0)
|
||||
self.add(empty_edge)
|
||||
|
||||
class FlatHeads(FlatCoin):
|
||||
CONFIG = {
|
||||
"fill_color": COLOR_HEADS_COIN,
|
||||
"symbol": "H",
|
||||
}
|
||||
|
||||
class FlatTails(FlatCoin):
|
||||
CONFIG = {
|
||||
"fill_color": COLOR_TAILS_COIN,
|
||||
"symbol": "T",
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
|
||||
class WhatDoesItReallyMean(TeacherStudentsScene):
|
||||
|
||||
CONFIG = {
|
||||
"default_pi_creature_kwargs": {
|
||||
"color": MAROON_E,
|
||||
"flip_at_start": True,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
student_q = TextMobject("What does", "``probability''", "\emph{actually}", "mean?")
|
||||
student_q.set_color_by_tex("probability", YELLOW)
|
||||
self.student_says(student_q, target_mode = "sassy")
|
||||
self.wait()
|
||||
|
||||
question_bubble = VGroup(student_q, students[1].bubble)
|
||||
scaled_qb = question_bubble.copy()
|
||||
scaled_qb.scale(0.4).to_corner(UL)
|
||||
self.play(Transform(question_bubble, scaled_qb))
|
||||
self.wait()
|
||||
|
||||
|
||||
|
||||
self.teacher_says("Don't worry -- philosophy can come later!")
|
||||
self.wait()
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class ComplexAnalysisOverlay(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject("Complex analysis")
|
||||
words.scale(1.25)
|
||||
words.to_edge(UP)
|
||||
words.add_background_rectangle()
|
||||
self.add(words)
|
||||
self.wait()
|
||||
|
||||
|
||||
class AnalyzeZSquared(ComplexTransformationScene, ZoomedScene):
|
||||
CONFIG = {
|
||||
"plane_config": {
|
||||
"line_frequency": 0.1,
|
||||
},
|
||||
"num_anchors_to_add_per_line": 20,
|
||||
"complex_homotopy": lambda z, t: z**(1.0 + t),
|
||||
"zoom_factor": 0.05,
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
ComplexTransformationScene.setup(self)
|
||||
ZoomedScene.setup(self)
|
||||
|
||||
def construct(self):
|
||||
self.edit_background_plane()
|
||||
self.add_title()
|
||||
# self.add_transforming_planes()
|
||||
# self.preview_some_numbers()
|
||||
self.zoom_in_to_one_plus_half_i()
|
||||
self.write_derivative()
|
||||
|
||||
def add_title(self):
|
||||
title = TexMobject("z \\rightarrow z^2")
|
||||
title.add_background_rectangle()
|
||||
title.scale(1.5)
|
||||
title.to_corner(UL, buff=MED_SMALL_BUFF)
|
||||
self.add_foreground_mobject(title)
|
||||
|
||||
def edit_background_plane(self):
|
||||
self.backgrounds.set_stroke(GREY, 2)
|
||||
self.background.secondary_lines.set_stroke(DARK_GREY, 1)
|
||||
self.add_foreground_mobject(self.background.coordinate_labels)
|
||||
|
||||
def add_transforming_planes(self):
|
||||
self.plane = self.get_plane()
|
||||
self.add_transformable_mobjects(self.plane)
|
||||
|
||||
def preview_some_numbers(self):
|
||||
dots = VGroup(*[
|
||||
Dot().move_to(self.background.number_to_point(z))
|
||||
for z in [
|
||||
1, 2, complex(0, 1),
|
||||
-1, complex(2, 0.5), complex(-1, -1), complex(3, 0.5),
|
||||
]
|
||||
])
|
||||
dots.set_color_by_gradient(RED, YELLOW)
|
||||
d_angle = 30 * DEGREES
|
||||
|
||||
dot_groups = VGroup()
|
||||
for dot in dots:
|
||||
point = dot.get_center()
|
||||
z = self.background.point_to_number(point)
|
||||
z_out = self.complex_homotopy(z, 1)
|
||||
out_point = self.background.number_to_point(z_out)
|
||||
path_arc = angle_of_vector(point)
|
||||
if abs(z - 1) < 0.01:
|
||||
# One is special
|
||||
arrow = Arc(
|
||||
start_angle=(-90 * DEGREES + d_angle),
|
||||
angle=(360 * DEGREES - 2 * d_angle),
|
||||
radius=0.25
|
||||
)
|
||||
arrow.add_tip(tip_length=0.15)
|
||||
arrow.pointwise_become_partial(arrow, 0, 0.9)
|
||||
arrow.next_to(dot, UP, buff=0)
|
||||
else:
|
||||
arrow = Arrow(
|
||||
point, out_point,
|
||||
path_arc=path_arc,
|
||||
buff=SMALL_BUFF,
|
||||
)
|
||||
arrow.match_color(dot)
|
||||
|
||||
out_dot = dot.copy()
|
||||
# out_dot.set_fill(opacity=0.5)
|
||||
out_dot.set_stroke(BLUE, 1)
|
||||
out_dot.move_to(out_point)
|
||||
dot.path_arc = path_arc
|
||||
dot.out_dot = out_dot
|
||||
|
||||
dot_group = VGroup(dot, arrow, out_dot)
|
||||
dot_groups.add(dot_group)
|
||||
|
||||
dot_copy = dot.copy()
|
||||
dot.save_state()
|
||||
dot.scale(3)
|
||||
dot.fade(1)
|
||||
|
||||
dot_group.anim = Succession(
|
||||
ApplyMethod(dot.restore),
|
||||
AnimationGroup(
|
||||
ShowCreation(arrow),
|
||||
ReplacementTransform(
|
||||
dot_copy, out_dot,
|
||||
path_arc=path_arc
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
for dot_group in dot_groups[:3]:
|
||||
self.play(dot_group.anim)
|
||||
self.wait()
|
||||
self.play(*[dg.anim for dg in dot_groups[3:]])
|
||||
|
||||
self.apply_complex_homotopy(
|
||||
self.complex_homotopy,
|
||||
added_anims=[Animation(dot_groups)]
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeOut(dot_groups))
|
||||
self.wait()
|
||||
self.play(FadeOut(self.plane))
|
||||
self.transformable_mobjects.remove(self.plane)
|
||||
|
||||
def zoom_in_to_one_plus_half_i(self):
|
||||
z = complex(1, 0.5)
|
||||
point = self.background.number_to_point(z)
|
||||
point_mob = VectorizedPoint(point)
|
||||
frame = self.zoomed_camera.frame
|
||||
frame.move_to(point)
|
||||
tiny_plane = NumberPlane(
|
||||
x_radius=2, y_radius=2,
|
||||
color=GREEN,
|
||||
secondary_color=GREEN_E
|
||||
)
|
||||
tiny_plane.replace(frame)
|
||||
|
||||
plane = self.get_plane()
|
||||
|
||||
words = TextMobject("What does this look like")
|
||||
words.add_background_rectangle()
|
||||
words.next_to(self.zoomed_display, LEFT, aligned_edge=UP)
|
||||
arrow = Arrow(words.get_bottom(), self.zoomed_display.get_left())
|
||||
VGroup(words, arrow).set_color(YELLOW)
|
||||
|
||||
self.play(FadeIn(plane))
|
||||
self.activate_zooming(animate=True)
|
||||
self.play(ShowCreation(tiny_plane))
|
||||
self.wait()
|
||||
self.add_transformable_mobjects(plane, tiny_plane, point_mob)
|
||||
self.add_foreground_mobjects(words, arrow)
|
||||
self.apply_complex_homotopy(
|
||||
self.complex_homotopy,
|
||||
added_anims=[
|
||||
Write(words),
|
||||
GrowArrow(arrow),
|
||||
MaintainPositionRelativeTo(frame, point_mob)
|
||||
]
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
def write_derivative(self):
|
||||
pass
|
||||
|
||||
# Helpers
|
||||
|
||||
def get_plane(self):
|
||||
top_plane = NumberPlane(
|
||||
y_radius=FRAME_HEIGHT / 2,
|
||||
x_line_frequency=0.1,
|
||||
y_line_frequency=0.1,
|
||||
)
|
||||
self.prepare_for_transformation(top_plane)
|
||||
bottom_plane = top_plane.copy()
|
||||
tiny_tiny_buff = 0.001
|
||||
top_plane.next_to(ORIGIN, UP, buff=tiny_tiny_buff)
|
||||
bottom_plane.next_to(ORIGIN, DOWN, buff=tiny_tiny_buff)
|
||||
return VGroup(top_plane, bottom_plane)
|
||||
@@ -1,107 +0,0 @@
|
||||
from active_projects.ode.part1.pendulum import *
|
||||
from active_projects.ode.part1.staging import *
|
||||
from active_projects.ode.part1.pi_scenes import *
|
||||
from active_projects.ode.part1.phase_space import *
|
||||
from active_projects.ode.part1.wordy_scenes import *
|
||||
|
||||
OUTPUT_DIRECTORY = "ode/part1"
|
||||
SCENES_IN_ORDER = [
|
||||
WhenChangeIsEasier,
|
||||
VectorFieldTest,
|
||||
IntroducePendulum,
|
||||
MultiplePendulumsOverlayed,
|
||||
PeriodFormula,
|
||||
FormulasAreLies,
|
||||
MediumAnglePendulum,
|
||||
MediumHighAnglePendulum,
|
||||
HighAnglePendulum,
|
||||
LowAnglePendulum,
|
||||
SomeOfYouWatching,
|
||||
SmallAngleApproximationTex,
|
||||
VeryLowAnglePendulum,
|
||||
FormulasAreLies,
|
||||
TourOfDifferentialEquations,
|
||||
WherePendulumLeads,
|
||||
LongDoublePendulum,
|
||||
# FollowThisThread,
|
||||
StrogatzQuote,
|
||||
ShowHorizontalDashedLine,
|
||||
RabbitFoxPopulations,
|
||||
RabbitFoxEquation,
|
||||
# Something...
|
||||
ShowSimpleTrajectory,
|
||||
SimpleProjectileEquation,
|
||||
SimpleProjectileEquationVGraphFreedom,
|
||||
ShowGravityAcceleration,
|
||||
UniversalGravityLawSymbols,
|
||||
ExampleTypicalODE,
|
||||
AnalyzePendulumForce,
|
||||
ShowSineValues,
|
||||
BuildUpEquation,
|
||||
AirResistanceBrace,
|
||||
ShowDerivativeVideo,
|
||||
SubtleAirCurrents,
|
||||
SimpleDampenedPendulum,
|
||||
DefineODE,
|
||||
SecondOrderEquationExample,
|
||||
ODEvsPDEinFrames,
|
||||
ProveTeacherWrong,
|
||||
SetAsideSeekingSolution,
|
||||
#
|
||||
WriteInRadians,
|
||||
XEqLThetaToCorner,
|
||||
ComingUp,
|
||||
InputLabel,
|
||||
SoWhatIsThetaThen,
|
||||
ReallyHardToSolve,
|
||||
ReasonForSolution,
|
||||
PhysicistPhaseSpace,
|
||||
GleickQuote,
|
||||
SpectrumOfStartingStates,
|
||||
WritePhaseFlow,
|
||||
AskAboutStability,
|
||||
LoveExample,
|
||||
PassageOfTime,
|
||||
LovePhaseSpace,
|
||||
ComparePhysicsToLove,
|
||||
FramesComparingPhysicsToLove,
|
||||
SetupToTakingManyTinySteps,
|
||||
ShowClutterPrevention,
|
||||
# VisualizeHeightSlopeCurvature,
|
||||
VisualizeStates,
|
||||
ReferencePiCollisionStateSpaces,
|
||||
IntroduceVectorField,
|
||||
XComponentArrows,
|
||||
BreakingSecondOrderIntoTwoFirstOrder,
|
||||
ShowPendulumPhaseFlow,
|
||||
ShowHighVelocityCase,
|
||||
TweakMuInFormula,
|
||||
TweakMuInVectorField,
|
||||
FromODEToVectorField,
|
||||
LorenzVectorField,
|
||||
ThreeBodiesInSpace,
|
||||
AltThreeBodiesInSpace,
|
||||
TwoBodiesInSpace,
|
||||
TwoBodiesWithZPart,
|
||||
ThreeBodyTitle,
|
||||
ThreeBodySymbols,
|
||||
#
|
||||
HighAmplitudePendulum,
|
||||
WritePhaseSpace,
|
||||
#
|
||||
AskAboutActuallySolving,
|
||||
WriteODESolvingCode,
|
||||
TakeManyTinySteps,
|
||||
ManyStepsFromDifferentStartingPoints,
|
||||
InaccurateComputation,
|
||||
HungerForExactness,
|
||||
ShowRect,
|
||||
ShowSquare,
|
||||
JumpToThisPoint,
|
||||
ThreeBodyEquation,
|
||||
ItGetsWorse,
|
||||
ChaosTitle,
|
||||
RevisitQuote,
|
||||
EndScreen,
|
||||
Thumbnail,
|
||||
]
|
||||
@@ -1,41 +0,0 @@
|
||||
from active_projects.ode.part2.staging import *
|
||||
from active_projects.ode.part2.fourier_series import *
|
||||
from active_projects.ode.part2.heat_equation import *
|
||||
from active_projects.ode.part2.pi_scenes import *
|
||||
from active_projects.ode.part2.wordy_scenes import *
|
||||
|
||||
OUTPUT_DIRECTORY = "ode/part2"
|
||||
SCENES_IN_ORDER = [
|
||||
PartTwoOfTour,
|
||||
HeatEquationIntroTitle,
|
||||
BrownianMotion,
|
||||
BlackScholes,
|
||||
ContrastChapters1And2,
|
||||
FourierSeriesIntro,
|
||||
FourierSeriesIntroBackground20,
|
||||
ExplainCircleAnimations,
|
||||
# FourierSeriesIntroBackground4,
|
||||
# FourierSeriesIntroBackground8,
|
||||
# FourierSeriesIntroBackground12,
|
||||
TwoDBodyWithManyTemperatures,
|
||||
TwoDBodyWithManyTemperaturesGraph,
|
||||
TwoDBodyWithManyTemperaturesContour,
|
||||
BringTwoRodsTogether,
|
||||
ShowEvolvingTempGraphWithArrows,
|
||||
# TodaysTargetWrapper,
|
||||
WriteHeatEquation,
|
||||
ReactionsToInitialHeatEquation,
|
||||
TalkThrough1DHeatGraph,
|
||||
ShowCubeFormation,
|
||||
CompareInputsOfGeneralCaseTo1D,
|
||||
ContrastXChangesToTChanges,
|
||||
ShowPartialDerivativeSymbols,
|
||||
WriteHeatEquation,
|
||||
ShowCurvatureToRateOfChangeIntuition,
|
||||
ContrastPDEToODE,
|
||||
TransitionToTempVsTime,
|
||||
Show1DAnd3DEquations,
|
||||
#
|
||||
AskAboutWhereEquationComesFrom,
|
||||
DiscreteSetup,
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,515 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.shared_constructs import *
|
||||
|
||||
|
||||
class SomeOfYouWatching(TeacherStudentsScene):
|
||||
CONFIG = {
|
||||
"camera_config": {
|
||||
"background_color": DARKER_GREY,
|
||||
}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
screen = self.screen
|
||||
screen.scale(1.25, about_edge=UL)
|
||||
screen.set_fill(BLACK, 1)
|
||||
self.add(screen)
|
||||
|
||||
self.teacher.change("raise_right_hand")
|
||||
for student in self.students:
|
||||
student.change("pondering", screen)
|
||||
|
||||
self.student_says(
|
||||
"Well...yeah",
|
||||
target_mode="tease"
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class FormulasAreLies(PiCreatureScene):
|
||||
def construct(self):
|
||||
you = self.pi_creature
|
||||
t2c = {
|
||||
"{L}": BLUE,
|
||||
"{g}": YELLOW,
|
||||
"\\theta_0": WHITE,
|
||||
"\\sqrt{\\,": WHITE,
|
||||
}
|
||||
kwargs = {"tex_to_color_map": t2c}
|
||||
period_eq = TexMobject(
|
||||
"\\text{Period} = 2\\pi \\sqrt{\\,{L} / {g}}",
|
||||
**kwargs
|
||||
)
|
||||
theta_eq = TexMobject(
|
||||
"\\theta(t) = \\theta_0 \\cos\\left("
|
||||
"\\sqrt{\\,{L} / {g}} \\cdot t"
|
||||
"\\right)",
|
||||
**kwargs
|
||||
)
|
||||
equations = VGroup(theta_eq, period_eq)
|
||||
equations.arrange(DOWN, buff=LARGE_BUFF)
|
||||
|
||||
for eq in period_eq, theta_eq:
|
||||
i = eq.index_of_part_by_tex("\\sqrt")
|
||||
eq.sqrt_part = eq[i:i + 4]
|
||||
|
||||
theta0 = theta_eq.get_part_by_tex("\\theta_0")
|
||||
theta0_words = TextMobject("Starting angle")
|
||||
theta0_words.next_to(theta0, UL)
|
||||
theta0_words.shift(UP + 0.5 * RIGHT)
|
||||
arrow = Arrow(
|
||||
theta0_words.get_bottom(),
|
||||
theta0,
|
||||
color=WHITE,
|
||||
tip_length=0.25,
|
||||
)
|
||||
|
||||
bubble = SpeechBubble()
|
||||
bubble.pin_to(you)
|
||||
bubble.write("Lies!")
|
||||
bubble.content.scale(2)
|
||||
bubble.resize_to_content()
|
||||
|
||||
self.add(period_eq)
|
||||
you.change("pondering", period_eq)
|
||||
self.wait()
|
||||
theta_eq.remove(*theta_eq.sqrt_part)
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
period_eq.sqrt_part,
|
||||
theta_eq.sqrt_part,
|
||||
),
|
||||
FadeIn(theta_eq)
|
||||
)
|
||||
theta_eq.add(*theta_eq.sqrt_part)
|
||||
self.play(
|
||||
FadeInFrom(theta0_words, LEFT),
|
||||
GrowArrow(arrow),
|
||||
)
|
||||
self.wait()
|
||||
self.play(you.change, "confused")
|
||||
self.wait()
|
||||
self.play(
|
||||
you.change, "angry",
|
||||
ShowCreation(bubble),
|
||||
FadeInFromPoint(bubble.content, you.mouth),
|
||||
equations.to_edge, LEFT,
|
||||
FadeOut(arrow),
|
||||
FadeOut(theta0_words),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def create_pi_creature(self):
|
||||
return You().flip().to_corner(DR)
|
||||
|
||||
|
||||
# class TourOfDifferentialEquations(Scene):
|
||||
# def construct(self):
|
||||
# pass
|
||||
|
||||
|
||||
class SoWhatIsThetaThen(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
ode = get_ode()
|
||||
ode.to_corner(UL)
|
||||
self.add(ode)
|
||||
|
||||
self.student_says(
|
||||
"Okay, but then\\\\"
|
||||
"what \\emph{is} $\\theta(t)$?"
|
||||
)
|
||||
self.wait()
|
||||
self.play(self.teacher.change, "happy")
|
||||
self.wait(2)
|
||||
self.teacher_says(
|
||||
"First, you must appreciate\\\\"
|
||||
"a deep truth...",
|
||||
added_anims=[self.get_student_changes(
|
||||
*3 * ["confused"]
|
||||
)]
|
||||
)
|
||||
self.wait(4)
|
||||
|
||||
|
||||
class ProveTeacherWrong(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
tex_config = {
|
||||
"tex_to_color_map": {
|
||||
"{\\theta}": BLUE,
|
||||
"{\\dot\\theta}": YELLOW,
|
||||
"{\\ddot\\theta}": RED,
|
||||
}
|
||||
}
|
||||
func = TexMobject(
|
||||
"{\\theta}(t)", "=",
|
||||
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
|
||||
**tex_config,
|
||||
)
|
||||
d_func = TexMobject(
|
||||
"{\\dot\\theta}(t)", "=",
|
||||
"-\\left(\\sqrt{g / L}\\right)",
|
||||
"\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)",
|
||||
**tex_config,
|
||||
)
|
||||
dd_func = TexMobject(
|
||||
"{\\ddot\\theta}(t)", "=",
|
||||
"-\\left(g / L\\right)",
|
||||
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
|
||||
**tex_config,
|
||||
)
|
||||
# ode = TexMobject(
|
||||
# "\\ddot {\\theta}({t})", "=",
|
||||
# "-\\mu \\dot {\\theta}({t})",
|
||||
# "-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
||||
# **tex_config,
|
||||
# )
|
||||
ode = get_ode()
|
||||
arrows = [TexMobject("\\Downarrow") for x in range(2)]
|
||||
|
||||
VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7)
|
||||
|
||||
teacher = self.teacher
|
||||
you = self.students[2]
|
||||
|
||||
self.student_thinks(ode)
|
||||
you.add_updater(lambda m: m.look_at(func))
|
||||
self.teacher_holds_up(func)
|
||||
self.wait()
|
||||
|
||||
group = VGroup(arrows[0], d_func, arrows[1], dd_func)
|
||||
group.arrange(DOWN)
|
||||
group.move_to(func, DOWN)
|
||||
|
||||
arrow = Arrow(
|
||||
group.get_corner(UL),
|
||||
ode.get_top(),
|
||||
path_arc=PI / 2,
|
||||
)
|
||||
q_marks = VGroup(*[
|
||||
TexMobject("?").scale(1.5).next_to(
|
||||
arrow.point_from_proportion(a),
|
||||
UP
|
||||
)
|
||||
for a in np.linspace(0.2, 0.8, 5)
|
||||
])
|
||||
cycle_animation(VFadeInThenOut(
|
||||
q_marks,
|
||||
lag_ratio=0.2,
|
||||
run_time=4,
|
||||
rate_func=squish_rate_func(smooth, 0, 0.5)
|
||||
))
|
||||
|
||||
self.play(
|
||||
func.next_to, group, UP,
|
||||
LaggedStartMap(
|
||||
FadeInFrom, group,
|
||||
lambda m: (m, UP)
|
||||
),
|
||||
teacher.change, "guilty",
|
||||
you.change, "sassy",
|
||||
)
|
||||
|
||||
rect = SurroundingRectangle(
|
||||
VGroup(group, func)
|
||||
)
|
||||
dashed_rect = DashedVMobject(rect, num_dashes=75)
|
||||
animated_rect = AnimatedBoundary(dashed_rect, cycle_rate=1)
|
||||
|
||||
self.wait()
|
||||
self.add(animated_rect, q_marks)
|
||||
self.play(
|
||||
ShowCreation(arrow),
|
||||
# FadeInFromDown(q_mark),
|
||||
self.get_student_changes("confused", "confused")
|
||||
)
|
||||
self.wait(4)
|
||||
self.change_student_modes(
|
||||
*3 * ["pondering"],
|
||||
self.teacher.change, "maybe"
|
||||
)
|
||||
self.wait(8)
|
||||
|
||||
|
||||
class PhysicistPhaseSpace(PiCreatureScene):
|
||||
def construct(self):
|
||||
physy = self.pi_creature
|
||||
name = TextMobject("Physicist")
|
||||
name.scale(1.5)
|
||||
name.to_corner(DL, buff=MED_SMALL_BUFF)
|
||||
physy.next_to(name, UP, SMALL_BUFF)
|
||||
VGroup(name, physy).shift_onto_screen()
|
||||
|
||||
axes = Axes(
|
||||
x_min=-1,
|
||||
x_max=10,
|
||||
y_min=-1,
|
||||
y_max=7,
|
||||
)
|
||||
axes.set_height(6)
|
||||
axes.next_to(physy, RIGHT)
|
||||
axes.to_edge(UP)
|
||||
axes.set_stroke(width=1)
|
||||
x_label = TextMobject("Position")
|
||||
x_label.next_to(axes.x_axis.get_right(), UP)
|
||||
y_label = TextMobject("Momentum")
|
||||
y_label.next_to(axes.y_axis.get_top(), RIGHT)
|
||||
|
||||
title = TextMobject("Phase space")
|
||||
title.scale(1.5)
|
||||
title.set_color(YELLOW)
|
||||
title.move_to(axes)
|
||||
|
||||
self.add(name, physy)
|
||||
|
||||
self.play(
|
||||
physy.change, "angry",
|
||||
Write(axes),
|
||||
FadeInFromDown(title)
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
GrowFromPoint(x_label, physy.get_corner(UR)),
|
||||
physy.change, "raise_right_hand",
|
||||
axes.x_axis.get_right()
|
||||
)
|
||||
self.play(
|
||||
GrowFromPoint(y_label, physy.get_corner(UR)),
|
||||
physy.look_at, axes.y_axis.get_top(),
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
def create_pi_creature(self):
|
||||
return PiCreature(color=GREY).to_corner(DL)
|
||||
|
||||
|
||||
class AskAboutActuallySolving(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
ode = get_ode()
|
||||
ode.to_corner(UL)
|
||||
self.add(ode)
|
||||
morty = self.teacher
|
||||
|
||||
self.student_says(
|
||||
"Yeah yeah, but how do\\\\"
|
||||
"you actually \\emph{solve} it?",
|
||||
student_index=1,
|
||||
target_mode="sassy",
|
||||
added_anims=[morty.change, "thinking"],
|
||||
)
|
||||
self.change_student_modes(
|
||||
"confused", "sassy", "confused",
|
||||
look_at_arg=ode,
|
||||
)
|
||||
self.wait()
|
||||
self.teacher_says(
|
||||
"What do you mean\\\\ by ``solve''?",
|
||||
target_mode="speaking",
|
||||
added_anims=[self.get_student_changes(
|
||||
*3 * ["erm"]
|
||||
)]
|
||||
)
|
||||
self.play(self.students[1].change, "angry")
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class HungerForExactness(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
students = self.students
|
||||
you = students[2]
|
||||
teacher = self.teacher
|
||||
|
||||
ode = get_ode()
|
||||
ode.to_corner(UL)
|
||||
left_part = ode[:5]
|
||||
friction_part = ode[5:11]
|
||||
self.add(ode)
|
||||
|
||||
proposed_solution = TexMobject(
|
||||
"\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}"
|
||||
)
|
||||
proposed_solution.next_to(
|
||||
you.get_corner(UL), UP, buff=0.7
|
||||
)
|
||||
proposed_solution_rect = SurroundingRectangle(
|
||||
proposed_solution, buff=MED_SMALL_BUFF,
|
||||
)
|
||||
proposed_solution_rect.set_color(BLUE)
|
||||
proposed_solution_rect.round_corners()
|
||||
|
||||
solution_p1 = TexMobject(
|
||||
"""
|
||||
\\theta(t) = 2\\text{am}\\left(
|
||||
\\frac{\\sqrt{2g + Lc_1} (t + c_2)}{2\\sqrt{L}},
|
||||
\\frac{4g}{2g + Lc_1}
|
||||
\\right)
|
||||
""",
|
||||
)
|
||||
solution_p1.to_corner(UL)
|
||||
solution_p2 = TexMobject(
|
||||
"c_1, c_2 = \\text{Constants depending on initial conditions}"
|
||||
)
|
||||
solution_p2.set_color(LIGHT_GREY)
|
||||
solution_p2.scale(0.75)
|
||||
solution_p3 = TexMobject(
|
||||
"""
|
||||
\\text{am}(u, k) =
|
||||
\\int_0^u \\text{dn}(v, k)\\,dv
|
||||
"""
|
||||
)
|
||||
solution_p3.name = TextMobject(
|
||||
"(Jacobi amplitude function)"
|
||||
)
|
||||
solution_p4 = TexMobject(
|
||||
"""
|
||||
\\text{dn}(u, k) =
|
||||
\\sqrt{1 - k^2 \\sin^2(\\phi)}
|
||||
"""
|
||||
)
|
||||
solution_p4.name = TextMobject(
|
||||
"(Jacobi elliptic function)"
|
||||
)
|
||||
solution_p5 = TextMobject("Where $\\phi$ satisfies")
|
||||
solution_p6 = TexMobject(
|
||||
"""
|
||||
u = \\int_0^\\phi \\frac{dt}{\\sqrt{1 - k^2 \\sin^2(t)}}
|
||||
"""
|
||||
)
|
||||
|
||||
solution = VGroup(
|
||||
solution_p1,
|
||||
solution_p2,
|
||||
solution_p3,
|
||||
solution_p4,
|
||||
solution_p5,
|
||||
solution_p6,
|
||||
)
|
||||
solution.arrange(DOWN)
|
||||
solution.scale(0.7)
|
||||
solution.to_corner(UL, buff=MED_SMALL_BUFF)
|
||||
solution.set_stroke(width=0, background=True)
|
||||
|
||||
solution.remove(solution_p2)
|
||||
solution_p1.add(solution_p2)
|
||||
solution.remove(solution_p5)
|
||||
solution_p6.add(solution_p5)
|
||||
|
||||
for part in [solution_p3, solution_p4]:
|
||||
part.name.scale(0.7 * 0.7)
|
||||
part.name.set_color(LIGHT_GREY)
|
||||
part.name.next_to(part, RIGHT)
|
||||
part.add(part.name)
|
||||
|
||||
self.student_says(
|
||||
"Right, but like,\\\\"
|
||||
"what \\emph{is} $\\theta(t)$?",
|
||||
target_mode="sassy",
|
||||
added_anims=[teacher.change, "guilty"],
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeInFromDown(proposed_solution),
|
||||
RemovePiCreatureBubble(
|
||||
you,
|
||||
target_mode="raise_left_hand",
|
||||
look_at_arg=proposed_solution,
|
||||
),
|
||||
teacher.change, "pondering",
|
||||
students[0].change, "pondering",
|
||||
students[1].change, "hesitant",
|
||||
)
|
||||
self.play(ShowCreation(proposed_solution_rect))
|
||||
self.play(
|
||||
proposed_solution.shift, 3 * RIGHT,
|
||||
proposed_solution_rect.shift, 3 * RIGHT,
|
||||
you.change, "raise_right_hand", teacher.eyes,
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
self.play(
|
||||
FadeOut(proposed_solution),
|
||||
FadeOut(proposed_solution_rect),
|
||||
ode.move_to, self.hold_up_spot, DOWN,
|
||||
ode.shift, LEFT,
|
||||
teacher.change, "raise_right_hand",
|
||||
self.get_student_changes(*3 * ["pondering"])
|
||||
)
|
||||
self.wait()
|
||||
ode.save_state()
|
||||
self.play(
|
||||
left_part.move_to, friction_part, RIGHT,
|
||||
left_part.match_y, left_part,
|
||||
friction_part.to_corner, DR,
|
||||
friction_part.fade, 0.5,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
modes = ["erm", "sad", "sad", "horrified"]
|
||||
for part, mode in zip(solution, modes):
|
||||
self.play(
|
||||
FadeInFrom(part, UP),
|
||||
self.get_student_changes(
|
||||
*3 * [mode],
|
||||
look_at_arg=part,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
self.wait(3)
|
||||
self.change_student_modes("tired", "sad", "concerned_musician")
|
||||
self.wait(4)
|
||||
self.look_at(solution)
|
||||
self.wait(5)
|
||||
self.play(
|
||||
FadeOutAndShift(solution, 2 * LEFT),
|
||||
Restore(ode),
|
||||
self.get_student_changes(
|
||||
"sick", "angry", "tired",
|
||||
)
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
mystery = TexMobject(
|
||||
"\\theta(t) = ???",
|
||||
tex_to_color_map={"\\theta": BLUE},
|
||||
)
|
||||
mystery.scale(2)
|
||||
mystery.to_edge(UP)
|
||||
mystery.set_stroke(width=0, background=True)
|
||||
mystery_boundary = AnimatedBoundary(
|
||||
mystery, stroke_width=1
|
||||
)
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(mystery),
|
||||
self.teacher.change, "pondering"
|
||||
)
|
||||
self.add(mystery_boundary, mystery)
|
||||
self.change_all_student_modes("sad")
|
||||
self.look_at(mystery)
|
||||
self.wait(5)
|
||||
|
||||
# Define
|
||||
self.student_says(
|
||||
"Let $\\text{P}(\\mu, g, L; t)$ be a\\\\"
|
||||
"function satisfying this ODE.",
|
||||
student_index=0,
|
||||
target_mode="speaking",
|
||||
added_anims=[
|
||||
FadeOut(mystery),
|
||||
FadeOut(mystery_boundary),
|
||||
ode.to_corner, UR
|
||||
]
|
||||
)
|
||||
self.change_student_modes(
|
||||
"hooray", "sassy", "sassy",
|
||||
look_at_arg=students[0].eyes.get_corner(UR),
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
|
||||
class ItGetsWorse(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.teacher_says("It gets\\\\worse")
|
||||
self.change_student_modes(
|
||||
"hesitant", "pleading", "erm"
|
||||
)
|
||||
self.wait(5)
|
||||
@@ -1,118 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
Lg_formula_config = {
|
||||
"tex_to_color_map": {
|
||||
"\\theta_0": WHITE,
|
||||
"{L}": BLUE,
|
||||
"{g}": YELLOW,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class You(PiCreature):
|
||||
CONFIG = {
|
||||
"color": BLUE_C,
|
||||
}
|
||||
|
||||
|
||||
def get_ode():
|
||||
tex_config = {
|
||||
"tex_to_color_map": {
|
||||
"{\\theta}": BLUE,
|
||||
"{\\dot\\theta}": RED,
|
||||
"{\\ddot\\theta}": YELLOW,
|
||||
"{t}": WHITE,
|
||||
"{\\mu}": WHITE,
|
||||
}
|
||||
}
|
||||
ode = TexMobject(
|
||||
"{\\ddot\\theta}({t})", "=",
|
||||
"-{\\mu} {\\dot\\theta}({t})",
|
||||
"-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
||||
**tex_config,
|
||||
)
|
||||
return ode
|
||||
|
||||
|
||||
def get_period_formula():
|
||||
return TexMobject(
|
||||
"2\\pi", "\\sqrt{\\,", "L", "/", "g", "}",
|
||||
tex_to_color_map={
|
||||
"L": BLUE,
|
||||
"g": YELLOW,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def pendulum_vector_field_func(point, mu=0.1, g=9.8, L=3):
|
||||
theta, omega = point[:2]
|
||||
return np.array([
|
||||
omega,
|
||||
-np.sqrt(g / L) * np.sin(theta) - mu * omega,
|
||||
0,
|
||||
])
|
||||
|
||||
|
||||
def get_vector_symbol(*texs, **kwargs):
|
||||
config = {
|
||||
"include_background_rectangle": True,
|
||||
"bracket_h_buff": SMALL_BUFF,
|
||||
"bracket_v_buff": SMALL_BUFF,
|
||||
"element_alignment_corner": ORIGIN,
|
||||
}
|
||||
config.update(kwargs)
|
||||
array = [[tex] for tex in texs]
|
||||
return Matrix(array, **config)
|
||||
|
||||
|
||||
def get_heart_var(index):
|
||||
heart = SuitSymbol("hearts")
|
||||
if index == 1:
|
||||
heart.set_color(BLUE_C)
|
||||
elif index == 2:
|
||||
heart.set_color(GREEN)
|
||||
heart.set_height(0.7)
|
||||
index = Integer(index)
|
||||
index.move_to(heart.get_corner(DR))
|
||||
heart.add(index)
|
||||
return heart
|
||||
|
||||
|
||||
def get_heart_var_deriv(index):
|
||||
heart = get_heart_var(index)
|
||||
filler_tex = "T"
|
||||
deriv = TexMobject("{d", filler_tex, "\\over", "dt}")
|
||||
deriv.scale(2)
|
||||
filler = deriv.get_part_by_tex(filler_tex)
|
||||
heart.match_height(filler)
|
||||
heart.move_to(filler)
|
||||
heart.scale(1.5, about_edge=UL)
|
||||
deriv.remove(filler)
|
||||
deriv.add(heart)
|
||||
deriv.heart = heart
|
||||
return deriv
|
||||
|
||||
|
||||
def get_love_equation1():
|
||||
equation = VGroup(
|
||||
get_heart_var_deriv(1),
|
||||
TexMobject("=").scale(2),
|
||||
TexMobject("a").scale(2),
|
||||
get_heart_var(2)
|
||||
)
|
||||
equation.arrange(RIGHT)
|
||||
equation[-1].shift(SMALL_BUFF * DL)
|
||||
return equation
|
||||
|
||||
|
||||
def get_love_equation2():
|
||||
equation = VGroup(
|
||||
get_heart_var_deriv(2),
|
||||
TexMobject("=").scale(2),
|
||||
TexMobject("-b").scale(2),
|
||||
get_heart_var(1),
|
||||
)
|
||||
equation.arrange(RIGHT)
|
||||
equation[-1].shift(SMALL_BUFF * DL)
|
||||
return equation
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,855 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.shared_constructs import *
|
||||
|
||||
|
||||
class SmallAngleApproximationTex(Scene):
|
||||
def construct(self):
|
||||
approx = TexMobject(
|
||||
"\\sin", "(", "\\theta", ") \\approx \\theta",
|
||||
tex_to_color_map={"\\theta": RED},
|
||||
arg_separator="",
|
||||
)
|
||||
|
||||
implies = TexMobject("\\Downarrow")
|
||||
period = TexMobject(
|
||||
"\\text{Period}", "\\approx",
|
||||
"2\\pi \\sqrt{\\,{L} / {g}}",
|
||||
**Lg_formula_config,
|
||||
)
|
||||
group = VGroup(approx, implies, period)
|
||||
group.arrange(DOWN)
|
||||
|
||||
approx_brace = Brace(approx, UP, buff=SMALL_BUFF)
|
||||
approx_words = TextMobject(
|
||||
"For small $\\theta$",
|
||||
tex_to_color_map={"$\\theta$": RED},
|
||||
)
|
||||
approx_words.scale(0.75)
|
||||
approx_words.next_to(approx_brace, UP, SMALL_BUFF)
|
||||
|
||||
self.add(approx, approx_brace, approx_words)
|
||||
self.play(
|
||||
Write(implies),
|
||||
FadeInFrom(period, LEFT)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class StrogatzQuote(Scene):
|
||||
def construct(self):
|
||||
quote = self.get_quote()
|
||||
movers = VGroup(*quote[:-1].family_members_with_points())
|
||||
for mover in movers:
|
||||
mover.save_state()
|
||||
disc = Circle(radius=0.05)
|
||||
disc.set_stroke(width=0)
|
||||
disc.set_fill(BLACK, 0)
|
||||
disc.move_to(mover)
|
||||
mover.become(disc)
|
||||
self.play(
|
||||
FadeInFrom(quote.author_part, LEFT),
|
||||
LaggedStartMap(
|
||||
# FadeInFromLarge,
|
||||
# quote[:-1].family_members_with_points(),
|
||||
Restore, movers,
|
||||
lag_ratio=0.005,
|
||||
run_time=2,
|
||||
)
|
||||
# FadeInFromDown(quote[:-1]),
|
||||
# lag_ratio=0.01,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(quote.law_part.copy().set_color(YELLOW)),
|
||||
run_time=1,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(quote.language_part.copy().set_color(BLUE)),
|
||||
run_time=1.5,
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
def get_quote(self):
|
||||
law_words = "laws of physics"
|
||||
language_words = "language of differential equations"
|
||||
author = "-Steven Strogatz"
|
||||
quote = TextMobject(
|
||||
"""
|
||||
\\Large
|
||||
``Since Newton, mankind has come to realize
|
||||
that the laws of physics are always expressed
|
||||
in the language of differential equations.''\\\\
|
||||
""" + author,
|
||||
alignment="",
|
||||
arg_separator=" ",
|
||||
substrings_to_isolate=[law_words, language_words, author]
|
||||
)
|
||||
quote.law_part = quote.get_part_by_tex(law_words)
|
||||
quote.language_part = quote.get_part_by_tex(language_words)
|
||||
quote.author_part = quote.get_part_by_tex(author)
|
||||
quote.set_width(12)
|
||||
quote.to_edge(UP)
|
||||
quote[-2].shift(SMALL_BUFF * LEFT)
|
||||
quote.author_part.shift(RIGHT + 0.5 * DOWN)
|
||||
quote.author_part.scale(1.2, about_edge=UL)
|
||||
|
||||
return quote
|
||||
|
||||
|
||||
class WriteInRadians(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject("In radians")
|
||||
words.set_color(YELLOW)
|
||||
square = SurroundingRectangle(TexMobject("\\theta"))
|
||||
square.next_to(words, UP)
|
||||
self.play(ShowCreation(square))
|
||||
self.play(Write(words), FadeOut(square))
|
||||
self.wait()
|
||||
|
||||
|
||||
class XEqLThetaToCorner(Scene):
|
||||
def construct(self):
|
||||
equation = TexMobject(
|
||||
"x = L\\theta",
|
||||
tex_to_color_map={
|
||||
"x": GREEN,
|
||||
"\\theta": BLUE,
|
||||
}
|
||||
)
|
||||
equation.move_to(DOWN + 3 * RIGHT)
|
||||
self.add(equation)
|
||||
self.play(equation.to_corner, DL, {"buff": LARGE_BUFF})
|
||||
self.wait()
|
||||
|
||||
|
||||
class ComingUp(Scene):
|
||||
CONFIG = {
|
||||
"camera_config": {"background_color": DARKER_GREY}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
frame = ScreenRectangle(
|
||||
stroke_width=0,
|
||||
fill_color=BLACK,
|
||||
fill_opacity=1,
|
||||
height=6
|
||||
)
|
||||
title = TextMobject("Coming up")
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
frame.next_to(title, DOWN)
|
||||
animated_frame = AnimatedBoundary(frame)
|
||||
self.add(frame, title, animated_frame)
|
||||
self.wait(10)
|
||||
|
||||
|
||||
class InputLabel(Scene):
|
||||
def construct(self):
|
||||
label = TextMobject("Input")
|
||||
label.scale(1.25)
|
||||
arrow = Vector(UP)
|
||||
arrow.next_to(label, UP)
|
||||
self.play(
|
||||
FadeInFrom(label, UP),
|
||||
GrowArrow(arrow)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ReallyHardToSolve(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject(
|
||||
"They're", "really\\\\",
|
||||
"freaking", "hard\\\\",
|
||||
"to", "solve\\\\",
|
||||
)
|
||||
words.set_height(6)
|
||||
|
||||
self.wait()
|
||||
for word in words:
|
||||
wait_time = 0.05 * len(word)
|
||||
self.add(word)
|
||||
self.wait(wait_time)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ReasonForSolution(Scene):
|
||||
def construct(self):
|
||||
# Words
|
||||
eq_word = TextMobject("Differential\\\\Equation")
|
||||
s_word = TextMobject("Solution")
|
||||
u_word = TextMobject("Understanding")
|
||||
c_word = TextMobject("Computation")
|
||||
cu_group = VGroup(u_word, c_word)
|
||||
cu_group.arrange(DOWN, buff=2)
|
||||
group = VGroup(eq_word, s_word, cu_group)
|
||||
group.arrange(RIGHT, buff=2)
|
||||
# words = VGroup(eq_word, s_word, u_word, c_word)
|
||||
|
||||
# Arrows
|
||||
arrows = VGroup(
|
||||
Arrow(eq_word.get_right(), s_word.get_left()),
|
||||
Arrow(s_word.get_right(), u_word.get_left()),
|
||||
Arrow(s_word.get_right(), c_word.get_left()),
|
||||
)
|
||||
arrows.set_color(LIGHT_GREY)
|
||||
new_arrows = VGroup(
|
||||
Arrow(
|
||||
eq_word.get_corner(UR),
|
||||
u_word.get_left(),
|
||||
path_arc=-60 * DEGREES,
|
||||
),
|
||||
Arrow(
|
||||
eq_word.get_corner(DR),
|
||||
c_word.get_left(),
|
||||
path_arc=60 * DEGREES,
|
||||
),
|
||||
)
|
||||
new_arrows.set_color(BLUE)
|
||||
|
||||
# Define first examples
|
||||
t2c = {
|
||||
"{x}": BLUE,
|
||||
"{\\dot x}": RED,
|
||||
}
|
||||
equation = TexMobject(
|
||||
"{\\dot x}(t) = k {x}(t)",
|
||||
tex_to_color_map=t2c,
|
||||
)
|
||||
equation.next_to(eq_word, DOWN)
|
||||
solution = TexMobject(
|
||||
"{x}(t) = x_0 e^{kt}",
|
||||
tex_to_color_map=t2c,
|
||||
)
|
||||
solution.next_to(s_word, DOWN, MED_LARGE_BUFF)
|
||||
equation.align_to(solution, DOWN)
|
||||
|
||||
axes = Axes(
|
||||
x_min=-1,
|
||||
x_max=5.5,
|
||||
y_min=-1,
|
||||
y_max=4.5,
|
||||
y_axis_config={"unit_size": 0.5}
|
||||
)
|
||||
axes.set_stroke(width=2)
|
||||
graph_line = axes.get_graph(
|
||||
lambda x: np.exp(0.4 * x)
|
||||
)
|
||||
graph_line.set_stroke(width=2)
|
||||
graph = VGroup(axes, graph_line)
|
||||
graph.scale(0.5)
|
||||
graph.next_to(u_word, UP)
|
||||
|
||||
computation = TexMobject(
|
||||
# "\\displaystyle "
|
||||
"e^x = \\sum_{n=0}^\\infty "
|
||||
"\\frac{x^n}{n!}"
|
||||
)
|
||||
computation.next_to(c_word, DOWN)
|
||||
|
||||
first_examples = VGroup(
|
||||
equation, solution, graph, computation
|
||||
)
|
||||
|
||||
# Second example
|
||||
ode = get_ode()
|
||||
ode.scale(0.75)
|
||||
second_examples = VGroup(
|
||||
ode,
|
||||
TexMobject("???").set_color(LIGHT_GREY),
|
||||
ScreenRectangle(
|
||||
height=2,
|
||||
stroke_width=1,
|
||||
),
|
||||
)
|
||||
for fe, se in zip(first_examples, second_examples):
|
||||
se.move_to(fe, DOWN)
|
||||
|
||||
ode.shift(2 * SMALL_BUFF * DOWN)
|
||||
ode.add_to_back(BackgroundRectangle(ode[-4:]))
|
||||
|
||||
self.add(eq_word)
|
||||
self.add(equation)
|
||||
self.play(
|
||||
FadeInFrom(s_word, LEFT),
|
||||
GrowArrow(arrows[0]),
|
||||
TransformFromCopy(equation, solution)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeInFrom(c_word, UL),
|
||||
GrowArrow(arrows[2]),
|
||||
FadeInFrom(computation, UP)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeInFrom(u_word, DL),
|
||||
GrowArrow(arrows[1]),
|
||||
FadeInFromDown(graph)
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
self.play(
|
||||
FadeOut(first_examples),
|
||||
FadeIn(second_examples[:2])
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
arrows.fade, 0.75,
|
||||
s_word.fade, 0.75,
|
||||
second_examples[1].fade, 0.75,
|
||||
ShowCreation(new_arrows[0]),
|
||||
FadeIn(second_examples[2])
|
||||
)
|
||||
self.play(
|
||||
ShowCreation(new_arrows[1]),
|
||||
Animation(second_examples),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class WritePhaseSpace(Scene):
|
||||
def construct(self):
|
||||
word = TextMobject("Phase space")
|
||||
word.scale(2)
|
||||
word.shift(FRAME_WIDTH * LEFT / 4)
|
||||
word.to_edge(UP)
|
||||
word.add_background_rectangle()
|
||||
|
||||
lines = VGroup(*[
|
||||
Line(v, 1.3 * v)
|
||||
for v in compass_directions(50)
|
||||
])
|
||||
lines.replace(word, stretch=True)
|
||||
lines.scale(1.5)
|
||||
lines.set_stroke(YELLOW)
|
||||
lines.shuffle()
|
||||
|
||||
self.add(word)
|
||||
self.play(
|
||||
ShowPassingFlashWithThinningStrokeWidth(
|
||||
lines,
|
||||
lag_ratio=0.002,
|
||||
run_time=1.5,
|
||||
time_width=0.9,
|
||||
n_segments=5,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class GleickQuote(Scene):
|
||||
def construct(self):
|
||||
quote = TextMobject(
|
||||
"``[Phase space is] one of the most\\\\",
|
||||
"powerful inventions", "of modern science.''\\\\",
|
||||
)
|
||||
quote.power_part = quote.get_part_by_tex("power")
|
||||
book = ImageMobject("ChaosBookCover")
|
||||
book.set_height(5)
|
||||
book.next_to(ORIGIN, LEFT)
|
||||
book.to_edge(DOWN)
|
||||
gleick = ImageMobject("JamesGleick")
|
||||
gleick.set_height(5)
|
||||
gleick.next_to(ORIGIN, RIGHT)
|
||||
gleick.to_edge(DOWN)
|
||||
quote.to_edge(UP)
|
||||
|
||||
self.play(
|
||||
FadeInFrom(book, RIGHT),
|
||||
FadeInFrom(gleick, LEFT),
|
||||
)
|
||||
self.wait()
|
||||
self.play(Write(quote))
|
||||
self.play(Write(
|
||||
quote.power_part.copy().set_color(BLUE),
|
||||
run_time=1
|
||||
))
|
||||
self.wait()
|
||||
|
||||
|
||||
class WritePhaseFlow(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject("Phase flow")
|
||||
words.scale(2)
|
||||
words.shift(FRAME_WIDTH * LEFT / 4)
|
||||
words.to_edge(UP)
|
||||
words.add_background_rectangle()
|
||||
self.play(Write(words))
|
||||
self.wait()
|
||||
|
||||
|
||||
class ShowSineValues(Scene):
|
||||
def construct(self):
|
||||
angle_tracker = ValueTracker(60 * DEGREES)
|
||||
get_angle = angle_tracker.get_value
|
||||
formula = always_redraw(
|
||||
lambda: self.get_sine_formula(get_angle())
|
||||
)
|
||||
self.add(formula)
|
||||
|
||||
self.play(
|
||||
angle_tracker.set_value, 0,
|
||||
run_time=3,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
angle_tracker.set_value, 90 * DEGREES,
|
||||
run_time=3,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def get_sine_formula(self, angle):
|
||||
sin, lp, rp = TexMobject(
|
||||
"\\sin", "(", ") = "
|
||||
)
|
||||
input_part = Integer(
|
||||
angle / DEGREES,
|
||||
unit="^\\circ",
|
||||
)
|
||||
input_part.set_color(YELLOW)
|
||||
output_part = DecimalNumber(
|
||||
np.sin(input_part.get_value() * DEGREES),
|
||||
num_decimal_places=3,
|
||||
)
|
||||
result = VGroup(
|
||||
sin, lp, input_part, rp, output_part
|
||||
)
|
||||
result.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
sin.scale(1.1, about_edge=DOWN)
|
||||
lp.align_to(rp, UP)
|
||||
return result
|
||||
|
||||
|
||||
class SetAsideSeekingSolution(Scene):
|
||||
def construct(self):
|
||||
ode = get_ode()
|
||||
ode.to_edge(UP)
|
||||
q1 = TextMobject("Find an exact solution")
|
||||
q1.set_color(YELLOW)
|
||||
q2 = TexMobject(
|
||||
"\\text{What is }", "\\theta", "(t)",
|
||||
"\\text{'s personality?}",
|
||||
tex_to_color_map={"\\theta": BLUE},
|
||||
arg_separator="",
|
||||
)
|
||||
theta = q2.get_part_by_tex("\\theta")
|
||||
|
||||
for q in q1, q2:
|
||||
q.scale(1.5)
|
||||
q.next_to(ode, DOWN, MED_LARGE_BUFF)
|
||||
eyes = Eyes(theta, height=0.1)
|
||||
|
||||
self.add(ode)
|
||||
self.add(q1)
|
||||
self.wait()
|
||||
self.play(
|
||||
q1.scale, 0.3,
|
||||
q1.to_corner, UR, MED_SMALL_BUFF,
|
||||
)
|
||||
self.play(FadeInFrom(q2, DOWN))
|
||||
self.play(
|
||||
eyes.blink,
|
||||
rate_func=lambda t: smooth(1 - t),
|
||||
)
|
||||
self.play(eyes.look_at, q2.get_left())
|
||||
self.play(eyes.look_at, q2.get_right())
|
||||
self.play(
|
||||
eyes.blink,
|
||||
rate_func=squish_rate_func(there_and_back)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
eyes.change_mode, "confused",
|
||||
eyes.look_at, ode.get_left(),
|
||||
)
|
||||
self.play(
|
||||
eyes.blink,
|
||||
rate_func=squish_rate_func(there_and_back)
|
||||
)
|
||||
|
||||
|
||||
class ThreeBodyTitle(Scene):
|
||||
def construct(self):
|
||||
title = TextMobject("Three body problem")
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
self.add(title)
|
||||
|
||||
|
||||
class ThreeBodySymbols(Scene):
|
||||
def construct(self):
|
||||
self.init_coord_groups()
|
||||
self.introduce_coord_groups()
|
||||
self.count_coords()
|
||||
|
||||
def init_coord_groups(self):
|
||||
kwargs = {
|
||||
"bracket_v_buff": 2 * SMALL_BUFF
|
||||
}
|
||||
positions = VGroup(*[
|
||||
get_vector_symbol(*[
|
||||
"{}_{}".format(s, i)
|
||||
for s in "xyz"
|
||||
], **kwargs)
|
||||
for i in range(1, 4)
|
||||
])
|
||||
velocities = VGroup(*[
|
||||
get_vector_symbol(*[
|
||||
"p^{}_{}".format(s, i)
|
||||
for s in "xyz"
|
||||
], **kwargs)
|
||||
for i in range(1, 4)
|
||||
])
|
||||
groups = VGroup(positions, velocities)
|
||||
colors = [GREEN, RED, BLUE]
|
||||
for group in groups:
|
||||
for matrix in group:
|
||||
matrix.coords = matrix.get_entries()
|
||||
for coord, color in zip(matrix.coords, colors):
|
||||
coord.set_color(color)
|
||||
group.arrange(RIGHT)
|
||||
groups.arrange(DOWN, buff=LARGE_BUFF)
|
||||
groups.to_edge(LEFT)
|
||||
|
||||
self.coord_groups = groups
|
||||
|
||||
def introduce_coord_groups(self):
|
||||
groups = self.coord_groups
|
||||
x_group, p_group = groups
|
||||
x_word = TextMobject("Positions")
|
||||
p_word = TextMobject("Momenta")
|
||||
words = VGroup(x_word, p_word)
|
||||
for word, group in zip(words, groups):
|
||||
word.next_to(group, UP)
|
||||
|
||||
rect_groups = VGroup()
|
||||
for group in groups:
|
||||
rect_group = VGroup(*[
|
||||
SurroundingRectangle(
|
||||
VGroup(*[
|
||||
tm.coords[i]
|
||||
for tm in group
|
||||
]),
|
||||
color=WHITE,
|
||||
stroke_width=2,
|
||||
)
|
||||
for i in range(3)
|
||||
])
|
||||
rect_groups.add(rect_group)
|
||||
|
||||
self.play(
|
||||
*[
|
||||
LaggedStartMap(
|
||||
FadeInFrom, group,
|
||||
lambda m: (m, UP),
|
||||
run_time=1,
|
||||
)
|
||||
for group in groups
|
||||
],
|
||||
*map(FadeInFromDown, words),
|
||||
)
|
||||
for rect_group in rect_groups:
|
||||
self.play(
|
||||
ShowCreationThenFadeOut(
|
||||
rect_group,
|
||||
lag_ratio=0.5,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def count_coords(self):
|
||||
coord_copies = VGroup()
|
||||
for group in self.coord_groups:
|
||||
for tex_mob in group:
|
||||
for coord in tex_mob.coords:
|
||||
coord_copy = coord.copy()
|
||||
coord_copy.set_stroke(
|
||||
WHITE, 2, background=True
|
||||
)
|
||||
coord_copies.add(coord_copy)
|
||||
|
||||
count = Integer()
|
||||
count_word = TextMobject("18", "degrees \\\\ of freedom")[1]
|
||||
count_group = VGroup(count, count_word)
|
||||
count_group.arrange(
|
||||
RIGHT,
|
||||
aligned_edge=DOWN,
|
||||
)
|
||||
count_group.scale(1.5)
|
||||
count_group.next_to(
|
||||
self.coord_groups, RIGHT,
|
||||
aligned_edge=DOWN,
|
||||
)
|
||||
count.add_updater(
|
||||
lambda m: m.set_value(len(coord_copies))
|
||||
)
|
||||
count.add_updater(
|
||||
lambda m: m.next_to(count_word[0][0], LEFT, aligned_edge=DOWN)
|
||||
)
|
||||
|
||||
self.add(count_group)
|
||||
self.play(
|
||||
# ChangeDecimalToValue(count, len(coord_copies)),
|
||||
ShowIncreasingSubsets(coord_copies),
|
||||
run_time=1.5,
|
||||
rate_func=linear,
|
||||
)
|
||||
self.play(FadeOut(coord_copies))
|
||||
|
||||
|
||||
class ThreeBodyEquation(Scene):
|
||||
def construct(self):
|
||||
x1 = "\\vec{\\textbf{x}}_1"
|
||||
x2 = "\\vec{\\textbf{x}}_2"
|
||||
x3 = "\\vec{\\textbf{x}}_3"
|
||||
kw = {
|
||||
"tex_to_color_map": {
|
||||
x1: RED,
|
||||
x2: GREEN,
|
||||
x3: BLUE,
|
||||
}
|
||||
}
|
||||
equations = VGroup(*[
|
||||
TexMobject(
|
||||
"{d^2", t1, "\\over dt^2}", "=",
|
||||
"G", "\\left("
|
||||
"{" + m2, "(", t2, "-", t1, ")"
|
||||
"\\over"
|
||||
"||", t2, "-", t1, "||^3}",
|
||||
"+",
|
||||
"{" + m3, "(", t3, "-", t1, ")"
|
||||
"\\over"
|
||||
"||", t3, "-", t1, "||^3}",
|
||||
"\\right)",
|
||||
**kw
|
||||
)
|
||||
for t1, t2, t3, m1, m2, m3 in [
|
||||
(x1, x2, x3, "m_1", "m_2", "m_3"),
|
||||
(x2, x3, x1, "m_2", "m_3", "m_1"),
|
||||
(x3, x1, x2, "m_3", "m_1", "m_2"),
|
||||
]
|
||||
])
|
||||
equations.arrange(DOWN, buff=LARGE_BUFF)
|
||||
|
||||
self.play(LaggedStartMap(
|
||||
FadeInFrom, equations,
|
||||
lambda m: (m, UP),
|
||||
lag_ratio=0.2,
|
||||
))
|
||||
self.wait()
|
||||
|
||||
|
||||
class JumpToThisPoint(Scene):
|
||||
def construct(self):
|
||||
dot = Dot(color=YELLOW)
|
||||
dot.scale(0.5)
|
||||
|
||||
arrow = Vector(DR, color=WHITE)
|
||||
arrow.next_to(dot, UL, SMALL_BUFF)
|
||||
words = TextMobject(
|
||||
"Jump directly to\\\\",
|
||||
"this point?",
|
||||
)
|
||||
words.add_background_rectangle_to_submobjects()
|
||||
words.next_to(arrow.get_start(), UP, SMALL_BUFF)
|
||||
|
||||
self.play(
|
||||
FadeInFromLarge(dot, 20),
|
||||
rate_func=rush_into,
|
||||
)
|
||||
self.play(
|
||||
GrowArrow(arrow),
|
||||
FadeInFromDown(words),
|
||||
)
|
||||
|
||||
|
||||
class ChaosTitle(Scene):
|
||||
def construct(self):
|
||||
title = TextMobject("Chaos theory")
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
line = Line(LEFT, RIGHT)
|
||||
line.set_width(FRAME_WIDTH - 1)
|
||||
line.next_to(title, DOWN)
|
||||
|
||||
self.play(
|
||||
Write(title),
|
||||
ShowCreation(line),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class RevisitQuote(StrogatzQuote, PiCreatureScene):
|
||||
def construct(self):
|
||||
quote = self.get_quote()
|
||||
quote.law_part.set_color(YELLOW)
|
||||
quote.language_part.set_color(BLUE)
|
||||
quote.set_stroke(BLACK, 6, background=True)
|
||||
quote.scale(0.8, about_edge=UL)
|
||||
|
||||
new_langauge_part = TextMobject(
|
||||
"\\Large Language of differential equations"
|
||||
)
|
||||
new_langauge_part.to_edge(UP)
|
||||
new_langauge_part.match_style(quote.language_part)
|
||||
|
||||
randy = self.pi_creature
|
||||
|
||||
self.play(
|
||||
FadeInFrom(quote[:-1], DOWN),
|
||||
FadeInFrom(quote[-1:], LEFT),
|
||||
randy.change, "raise_right_hand",
|
||||
)
|
||||
self.play(Blink(randy))
|
||||
self.play(randy.change, "angry")
|
||||
self.play(
|
||||
Blink(randy),
|
||||
VFadeOut(randy, run_time=3)
|
||||
)
|
||||
mover = VGroup(quote.language_part)
|
||||
self.add(quote, mover)
|
||||
self.play(
|
||||
ReplacementTransform(
|
||||
mover, new_langauge_part,
|
||||
),
|
||||
*[
|
||||
FadeOut(part)
|
||||
for part in quote
|
||||
if part is not quote.language_part
|
||||
],
|
||||
run_time=2,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class EndScreen(PatreonEndScreen):
|
||||
CONFIG = {
|
||||
"specific_patrons": [
|
||||
"Juan Benet",
|
||||
"Vassili Philippov",
|
||||
"Burt Humburg",
|
||||
"Carlos Vergara Del Rio",
|
||||
"Matt Russell",
|
||||
"Scott Gray",
|
||||
"soekul",
|
||||
"Tihan Seale",
|
||||
"Ali Yahya",
|
||||
"dave nicponski",
|
||||
"Evan Phillips",
|
||||
"Graham",
|
||||
"Joseph Kelly",
|
||||
"Kaustuv DeBiswas",
|
||||
"LambdaLabs",
|
||||
"Lukas Biewald",
|
||||
"Mike Coleman",
|
||||
"Peter Mcinerney",
|
||||
"Quantopian",
|
||||
"Roy Larson",
|
||||
"Scott Walter, Ph.D.",
|
||||
"Yana Chernobilsky",
|
||||
"Yu Jun",
|
||||
"Jordan Scales",
|
||||
"Lukas -krtek.net- Novy",
|
||||
"John Shaughnessy",
|
||||
"Britt Selvitelle",
|
||||
"David Gow",
|
||||
"J",
|
||||
"Jonathan Wilson",
|
||||
"Joseph John Cox",
|
||||
"Magnus Dahlström",
|
||||
"Randy C. Will",
|
||||
"Ryan Atallah",
|
||||
"Luc Ritchie",
|
||||
"1stViewMaths",
|
||||
"Adrian Robinson",
|
||||
"Alexis Olson",
|
||||
"Andreas Benjamin Brössel",
|
||||
"Andrew Busey",
|
||||
"Ankalagon",
|
||||
"Antonio Juarez",
|
||||
"Arjun Chakroborty",
|
||||
"Art Ianuzzi",
|
||||
"Awoo",
|
||||
"Bernd Sing",
|
||||
"Boris Veselinovich",
|
||||
"Brian Staroselsky",
|
||||
"Chad Hurst",
|
||||
"Charles Southerland",
|
||||
"Chris Connett",
|
||||
"Christian Kaiser",
|
||||
"Clark Gaebel",
|
||||
"Cooper Jones",
|
||||
"Danger Dai",
|
||||
"Dave B",
|
||||
"Dave Kester",
|
||||
"David Clark",
|
||||
"DeathByShrimp",
|
||||
"Delton Ding",
|
||||
"eaglle",
|
||||
"emptymachine",
|
||||
"Eric Younge",
|
||||
"Eryq Ouithaqueue",
|
||||
"Federico Lebron",
|
||||
"Giovanni Filippi",
|
||||
"Hal Hildebrand",
|
||||
"Herman Dieset",
|
||||
"Hitoshi Yamauchi",
|
||||
"Isaac Jeffrey Lee",
|
||||
"j eduardo perez",
|
||||
"Jacob Magnuson",
|
||||
"Jameel Syed",
|
||||
"Jason Hise",
|
||||
"Jeff Linse",
|
||||
"Jeff Straathof",
|
||||
"John Griffith",
|
||||
"John Haley",
|
||||
"John V Wertheim",
|
||||
"Jonathan Eppele",
|
||||
"Kai-Siang Ang",
|
||||
"Kanan Gill",
|
||||
"L0j1k",
|
||||
"Lee Redden",
|
||||
"Linh Tran",
|
||||
"Ludwig Schubert",
|
||||
"Magister Mugit",
|
||||
"Mark B Bahu",
|
||||
"Mark Heising",
|
||||
"Martin Price",
|
||||
"Mathias Jansson",
|
||||
"Matt Langford",
|
||||
"Matt Roveto",
|
||||
"Matthew Cocke",
|
||||
"Michael Faust",
|
||||
"Michael Hardel",
|
||||
"Mirik Gogri",
|
||||
"Mustafa Mahdi",
|
||||
"Márton Vaitkus",
|
||||
"Nero Li",
|
||||
"Nikita Lesnikov",
|
||||
"Omar Zrien",
|
||||
"Owen Campbell-Moore",
|
||||
"Peter Ehrnstrom",
|
||||
"RedAgent14",
|
||||
"rehmi post",
|
||||
"Richard Burgmann",
|
||||
"Richard Comish",
|
||||
"Ripta Pasay",
|
||||
"Rish Kundalia",
|
||||
"Robert Teed",
|
||||
"Roobie",
|
||||
"Ryan Williams",
|
||||
"Samuel D. Judge",
|
||||
"Solara570",
|
||||
"Stevie Metke",
|
||||
"Tal Einav",
|
||||
"Ted Suzman",
|
||||
"Valeriy Skobelev",
|
||||
"Xavier Bernard",
|
||||
"Yavor Ivanov",
|
||||
"Yaw Etse",
|
||||
"YinYangBalance.Asia",
|
||||
"Zach Cardwell",
|
||||
]
|
||||
}
|
||||
@@ -1,826 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
# import scipy
|
||||
|
||||
|
||||
class FourierCirclesScene(Scene):
|
||||
CONFIG = {
|
||||
"n_circles": 10,
|
||||
"big_radius": 2,
|
||||
"colors": [
|
||||
BLUE_D,
|
||||
BLUE_C,
|
||||
BLUE_E,
|
||||
GREY_BROWN,
|
||||
],
|
||||
"circle_style": {
|
||||
"stroke_width": 2,
|
||||
},
|
||||
"arrow_config": {
|
||||
"buff": 0,
|
||||
"max_tip_length_to_length_ratio": 0.35,
|
||||
"tip_length": 0.15,
|
||||
"max_stroke_width_to_length_ratio": 10,
|
||||
"stroke_width": 2,
|
||||
},
|
||||
"use_vectors": True,
|
||||
"base_frequency": 1,
|
||||
"slow_factor": 0.25,
|
||||
"center_point": ORIGIN,
|
||||
"parametric_function_step_size": 0.001,
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
self.slow_factor_tracker = ValueTracker(
|
||||
self.slow_factor
|
||||
)
|
||||
|
||||
def get_slow_factor(self):
|
||||
return self.slow_factor_tracker.get_value()
|
||||
|
||||
#
|
||||
def get_freqs(self):
|
||||
n = self.n_circles
|
||||
all_freqs = list(range(n // 2, -n // 2, -1))
|
||||
all_freqs.sort(key=abs)
|
||||
return all_freqs
|
||||
|
||||
def get_coefficients(self):
|
||||
return [complex(0) for x in range(self.n_circles)]
|
||||
|
||||
def get_color_iterator(self):
|
||||
return it.cycle(self.colors)
|
||||
|
||||
def get_circles(self, freqs=None, coefficients=None):
|
||||
circles = VGroup()
|
||||
color_iterator = self.get_color_iterator()
|
||||
self.center_tracker = VectorizedPoint(self.center_point)
|
||||
|
||||
if freqs is None:
|
||||
freqs = self.get_freqs()
|
||||
if coefficients is None:
|
||||
coefficients = self.get_coefficients()
|
||||
|
||||
last_circle = None
|
||||
for freq, coefficient in zip(freqs, coefficients):
|
||||
if last_circle:
|
||||
center_func = last_circle.get_start
|
||||
else:
|
||||
center_func = self.center_tracker.get_location
|
||||
circle = self.get_circle(
|
||||
coefficient=coefficient,
|
||||
freq=freq,
|
||||
color=next(color_iterator),
|
||||
center_func=center_func,
|
||||
)
|
||||
circles.add(circle)
|
||||
last_circle = circle
|
||||
return circles
|
||||
|
||||
def get_circle(self, coefficient, freq, color, center_func):
|
||||
radius = abs(coefficient)
|
||||
phase = np.log(coefficient).imag
|
||||
circle = Circle(
|
||||
radius=radius,
|
||||
color=color,
|
||||
**self.circle_style,
|
||||
)
|
||||
line_points = (
|
||||
circle.get_center(),
|
||||
circle.get_start(),
|
||||
)
|
||||
if self.use_vectors:
|
||||
circle.radial_line = Arrow(
|
||||
*line_points,
|
||||
**self.arrow_config,
|
||||
)
|
||||
else:
|
||||
circle.radial_line = Line(
|
||||
*line_points,
|
||||
color=WHITE,
|
||||
**self.circle_style,
|
||||
)
|
||||
circle.add(circle.radial_line)
|
||||
circle.freq = freq
|
||||
circle.phase = phase
|
||||
circle.rotate(phase)
|
||||
circle.coefficient = coefficient
|
||||
circle.center_func = center_func
|
||||
circle.add_updater(self.update_circle)
|
||||
return circle
|
||||
|
||||
def update_circle(self, circle, dt):
|
||||
circle.rotate(
|
||||
self.get_slow_factor() * circle.freq * dt * TAU
|
||||
)
|
||||
circle.move_to(circle.center_func())
|
||||
return circle
|
||||
|
||||
def get_rotating_vectors(self, circles):
|
||||
return VGroup(*[
|
||||
self.get_rotating_vector(circle)
|
||||
for circle in circles
|
||||
])
|
||||
|
||||
def get_rotating_vector(self, circle):
|
||||
vector = Vector(RIGHT, color=WHITE)
|
||||
vector.add_updater(lambda v, dt: v.put_start_and_end_on(
|
||||
circle.get_center(),
|
||||
circle.get_start(),
|
||||
))
|
||||
circle.vector = vector
|
||||
return vector
|
||||
|
||||
def get_circle_end_path(self, circles, color=YELLOW):
|
||||
coefs = [c.coefficient for c in circles]
|
||||
freqs = [c.freq for c in circles]
|
||||
center = circles[0].get_center()
|
||||
|
||||
path = ParametricFunction(
|
||||
lambda t: center + reduce(op.add, [
|
||||
complex_to_R3(
|
||||
coef * np.exp(TAU * 1j * freq * t)
|
||||
)
|
||||
for coef, freq in zip(coefs, freqs)
|
||||
]),
|
||||
t_min=0,
|
||||
t_max=1,
|
||||
color=color,
|
||||
step_size=self.parametric_function_step_size,
|
||||
)
|
||||
return path
|
||||
|
||||
# TODO, this should be a general animated mobect
|
||||
def get_drawn_path(self, circles, stroke_width=2, **kwargs):
|
||||
path = self.get_circle_end_path(circles, **kwargs)
|
||||
broken_path = CurvesAsSubmobjects(path)
|
||||
broken_path.curr_time = 0
|
||||
|
||||
def update_path(path, dt):
|
||||
alpha = path.curr_time * self.get_slow_factor()
|
||||
n_curves = len(path)
|
||||
for a, sp in zip(np.linspace(0, 1, n_curves), path):
|
||||
b = alpha - a
|
||||
if b < 0:
|
||||
width = 0
|
||||
else:
|
||||
width = stroke_width * (1 - (b % 1))
|
||||
sp.set_stroke(width=width)
|
||||
path.curr_time += dt
|
||||
return path
|
||||
|
||||
broken_path.set_color(YELLOW)
|
||||
broken_path.add_updater(update_path)
|
||||
return broken_path
|
||||
|
||||
def get_y_component_wave(self,
|
||||
circles,
|
||||
left_x=1,
|
||||
color=PINK,
|
||||
n_copies=2,
|
||||
right_shift_rate=5):
|
||||
path = self.get_circle_end_path(circles)
|
||||
wave = ParametricFunction(
|
||||
lambda t: op.add(
|
||||
right_shift_rate * t * LEFT,
|
||||
path.function(t)[1] * UP
|
||||
),
|
||||
t_min=path.t_min,
|
||||
t_max=path.t_max,
|
||||
color=color,
|
||||
)
|
||||
wave_copies = VGroup(*[
|
||||
wave.copy()
|
||||
for x in range(n_copies)
|
||||
])
|
||||
wave_copies.arrange(RIGHT, buff=0)
|
||||
top_point = wave_copies.get_top()
|
||||
wave.creation = ShowCreation(
|
||||
wave,
|
||||
run_time=(1 / self.get_slow_factor()),
|
||||
rate_func=linear,
|
||||
)
|
||||
cycle_animation(wave.creation)
|
||||
wave.add_updater(lambda m: m.shift(
|
||||
(m.get_left()[0] - left_x) * LEFT
|
||||
))
|
||||
|
||||
def update_wave_copies(wcs):
|
||||
index = int(
|
||||
wave.creation.total_time * self.get_slow_factor()
|
||||
)
|
||||
wcs[:index].match_style(wave)
|
||||
wcs[index:].set_stroke(width=0)
|
||||
wcs.next_to(wave, RIGHT, buff=0)
|
||||
wcs.align_to(top_point, UP)
|
||||
wave_copies.add_updater(update_wave_copies)
|
||||
|
||||
return VGroup(wave, wave_copies)
|
||||
|
||||
def get_wave_y_line(self, circles, wave):
|
||||
return DashedLine(
|
||||
circles[-1].get_start(),
|
||||
wave[0].get_end(),
|
||||
stroke_width=1,
|
||||
dash_length=DEFAULT_DASH_LENGTH * 0.5,
|
||||
)
|
||||
|
||||
# Computing Fourier series
|
||||
def get_coefficients_of_path(self, path, n_samples=10000, freqs=None):
|
||||
if freqs is None:
|
||||
freqs = self.get_freqs()
|
||||
dt = 1 / n_samples
|
||||
ts = np.arange(0, 1, dt)
|
||||
samples = np.array([
|
||||
path.point_from_proportion(t)
|
||||
for t in ts
|
||||
])
|
||||
samples -= self.center_point
|
||||
complex_samples = samples[:, 0] + 1j * samples[:, 1]
|
||||
|
||||
result = []
|
||||
for freq in freqs:
|
||||
riemann_sum = np.array([
|
||||
np.exp(-TAU * 1j * freq * t) * cs
|
||||
for t, cs in zip(ts, complex_samples)
|
||||
]).sum() * dt
|
||||
result.append(riemann_sum)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class FourierSeriesIntroBackground4(FourierCirclesScene):
|
||||
CONFIG = {
|
||||
"n_circles": 4,
|
||||
"center_point": 4 * LEFT,
|
||||
"run_time": 30,
|
||||
"big_radius": 1.5,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
circles = self.get_circles()
|
||||
path = self.get_drawn_path(circles)
|
||||
wave = self.get_y_component_wave(circles)
|
||||
h_line = always_redraw(
|
||||
lambda: self.get_wave_y_line(circles, wave)
|
||||
)
|
||||
|
||||
# Why?
|
||||
circles.update(-1 / self.camera.frame_rate)
|
||||
#
|
||||
self.add(circles, path, wave, h_line)
|
||||
self.wait(self.run_time)
|
||||
|
||||
def get_ks(self):
|
||||
return np.arange(1, 2 * self.n_circles + 1, 2)
|
||||
|
||||
def get_freqs(self):
|
||||
return self.base_frequency * self.get_ks()
|
||||
|
||||
def get_coefficients(self):
|
||||
return self.big_radius / self.get_ks()
|
||||
|
||||
|
||||
class FourierSeriesIntroBackground8(FourierSeriesIntroBackground4):
|
||||
CONFIG = {
|
||||
"n_circles": 8,
|
||||
}
|
||||
|
||||
|
||||
class FourierSeriesIntroBackground12(FourierSeriesIntroBackground4):
|
||||
CONFIG = {
|
||||
"n_circles": 12,
|
||||
}
|
||||
|
||||
|
||||
class FourierSeriesIntroBackground20(FourierSeriesIntroBackground4):
|
||||
CONFIG = {
|
||||
"n_circles": 20,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfPiSymbol(FourierCirclesScene):
|
||||
CONFIG = {
|
||||
"n_circles": 50,
|
||||
"center_point": ORIGIN,
|
||||
"slow_factor": 0.1,
|
||||
"run_time": 30,
|
||||
"tex": "\\pi",
|
||||
"start_drawn": False,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
path = self.get_path()
|
||||
coefs = self.get_coefficients_of_path(path)
|
||||
|
||||
circles = self.get_circles(coefficients=coefs)
|
||||
for k, circle in zip(it.count(1), circles):
|
||||
circle.set_stroke(width=max(
|
||||
1 / np.sqrt(k),
|
||||
1,
|
||||
))
|
||||
|
||||
# approx_path = self.get_circle_end_path(circles)
|
||||
drawn_path = self.get_drawn_path(circles)
|
||||
if self.start_drawn:
|
||||
drawn_path.curr_time = 1 / self.slow_factor
|
||||
|
||||
self.add(path)
|
||||
self.add(circles)
|
||||
self.add(drawn_path)
|
||||
self.wait(self.run_time)
|
||||
|
||||
def get_path(self):
|
||||
tex_mob = TexMobject(self.tex)
|
||||
tex_mob.set_height(6)
|
||||
path = tex_mob.family_members_with_points()[0]
|
||||
path.set_fill(opacity=0)
|
||||
path.set_stroke(WHITE, 1)
|
||||
return path
|
||||
|
||||
|
||||
class FourierOfPiSymbol5(FourierOfPiSymbol):
|
||||
CONFIG = {
|
||||
"n_circles": 5,
|
||||
"run_time": 10,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfTrebleClef(FourierOfPiSymbol):
|
||||
CONFIG = {
|
||||
"n_circles": 100,
|
||||
"run_time": 10,
|
||||
"start_drawn": True,
|
||||
"file_name": "TrebleClef",
|
||||
"height": 7.5,
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
shape = SVGMobject(self.file_name)
|
||||
return shape
|
||||
|
||||
def get_path(self):
|
||||
shape = self.get_shape()
|
||||
path = shape.family_members_with_points()[0]
|
||||
path.set_height(self.height)
|
||||
path.set_fill(opacity=0)
|
||||
path.set_stroke(WHITE, 0)
|
||||
return path
|
||||
|
||||
|
||||
class FourierOfIP(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"file_name": "IP_logo2",
|
||||
"height": 6,
|
||||
"n_circles": 100,
|
||||
}
|
||||
|
||||
# def construct(self):
|
||||
# path = self.get_path()
|
||||
# self.add(path)
|
||||
|
||||
def get_shape(self):
|
||||
shape = SVGMobject(self.file_name)
|
||||
return shape
|
||||
|
||||
def get_path(self):
|
||||
shape = self.get_shape()
|
||||
path = shape.family_members_with_points()[0]
|
||||
path.add_line_to(path.get_start())
|
||||
# path.make_smooth()
|
||||
|
||||
path.set_height(self.height)
|
||||
path.set_fill(opacity=0)
|
||||
path.set_stroke(WHITE, 0)
|
||||
return path
|
||||
|
||||
|
||||
class FourierOfEighthNote(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"file_name": "EighthNote"
|
||||
}
|
||||
|
||||
|
||||
class FourierOfN(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 6,
|
||||
"n_circles": 1000,
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
return TexMobject("N")
|
||||
|
||||
|
||||
class FourierNailAndGear(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 6,
|
||||
"n_circles": 200,
|
||||
"run_time": 100,
|
||||
"slow_factor": 0.01,
|
||||
"parametric_function_step_size": 0.0001,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
shape = SVGMobject("Nail_And_Gear")[1]
|
||||
return shape
|
||||
|
||||
|
||||
class FourierBatman(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 4,
|
||||
"n_circles": 100,
|
||||
"run_time": 10,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
shape = SVGMobject("BatmanLogo")[1]
|
||||
return shape
|
||||
|
||||
|
||||
class FourierHeart(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 4,
|
||||
"n_circles": 100,
|
||||
"run_time": 10,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
shape = SuitSymbol("hearts")
|
||||
return shape
|
||||
|
||||
def get_drawn_path(self, *args, **kwargs):
|
||||
kwargs["stroke_width"] = 5
|
||||
path = super().get_drawn_path(*args, **kwargs)
|
||||
path.set_color(PINK)
|
||||
return path
|
||||
|
||||
|
||||
class FourierNDQ(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 4,
|
||||
"n_circles": 1000,
|
||||
"run_time": 10,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
path = VMobject()
|
||||
shape = TexMobject("Hayley")
|
||||
for sp in shape.family_members_with_points():
|
||||
path.append_points(sp.points)
|
||||
return path
|
||||
|
||||
|
||||
class FourierGoogleG(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"n_circles": 10,
|
||||
"height": 5,
|
||||
"g_colors": [
|
||||
"#4285F4",
|
||||
"#DB4437",
|
||||
"#F4B400",
|
||||
"#0F9D58",
|
||||
]
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
g = SVGMobject("google_logo")[5]
|
||||
g.center()
|
||||
self.add(g)
|
||||
return g
|
||||
|
||||
def get_drawn_path(self, *args, **kwargs):
|
||||
kwargs["stroke_width"] = 7
|
||||
path = super().get_drawn_path(*args, **kwargs)
|
||||
|
||||
blue, red, yellow, green = self.g_colors
|
||||
|
||||
path[:250].set_color(blue)
|
||||
path[250:333].set_color(green)
|
||||
path[333:370].set_color(yellow)
|
||||
path[370:755].set_color(red)
|
||||
path[755:780].set_color(yellow)
|
||||
path[780:860].set_color(green)
|
||||
path[860:].set_color(blue)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
class ExplainCircleAnimations(FourierCirclesScene):
|
||||
CONFIG = {
|
||||
"n_circles": 100,
|
||||
"center_point": 2 * DOWN,
|
||||
"n_top_circles": 9,
|
||||
"path_height": 3,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_path()
|
||||
self.add_circles()
|
||||
self.wait(8)
|
||||
self.organize_circles_in_a_row()
|
||||
self.show_frequencies()
|
||||
self.show_examples_for_frequencies()
|
||||
self.show_as_vectors()
|
||||
self.show_vector_sum()
|
||||
self.tweak_starting_vectors()
|
||||
|
||||
def add_path(self):
|
||||
self.path = self.get_path()
|
||||
self.add(self.path)
|
||||
|
||||
def add_circles(self):
|
||||
coefs = self.get_coefficients_of_path(self.path)
|
||||
self.circles = self.get_circles(coefficients=coefs)
|
||||
|
||||
self.add(self.circles)
|
||||
self.drawn_path = self.get_drawn_path(self.circles)
|
||||
self.add(self.drawn_path)
|
||||
|
||||
def organize_circles_in_a_row(self):
|
||||
circles = self.circles
|
||||
top_circles = circles[:self.n_top_circles].copy()
|
||||
|
||||
center_trackers = VGroup()
|
||||
for circle in top_circles:
|
||||
tracker = VectorizedPoint(circle.center_func())
|
||||
circle.center_func = tracker.get_location
|
||||
center_trackers.add(tracker)
|
||||
tracker.freq = circle.freq
|
||||
tracker.circle = circle
|
||||
|
||||
center_trackers.submobjects.sort(
|
||||
key=lambda m: m.freq
|
||||
)
|
||||
center_trackers.generate_target()
|
||||
right_buff = 1.45
|
||||
center_trackers.target.arrange(RIGHT, buff=right_buff)
|
||||
center_trackers.target.to_edge(UP, buff=1.25)
|
||||
|
||||
self.add(top_circles)
|
||||
self.play(
|
||||
MoveToTarget(center_trackers),
|
||||
run_time=2
|
||||
)
|
||||
self.wait(4)
|
||||
|
||||
self.top_circles = top_circles
|
||||
self.center_trackers = center_trackers
|
||||
|
||||
def show_frequencies(self):
|
||||
center_trackers = self.center_trackers
|
||||
|
||||
freq_numbers = VGroup()
|
||||
for ct in center_trackers:
|
||||
number = Integer(ct.freq)
|
||||
number.next_to(ct, DOWN, buff=1)
|
||||
freq_numbers.add(number)
|
||||
ct.circle.number = number
|
||||
|
||||
ld, rd = [
|
||||
TexMobject("\\dots")
|
||||
for x in range(2)
|
||||
]
|
||||
ld.next_to(freq_numbers, LEFT, MED_LARGE_BUFF)
|
||||
rd.next_to(freq_numbers, RIGHT, MED_LARGE_BUFF)
|
||||
freq_numbers.add_to_back(ld)
|
||||
freq_numbers.add(rd)
|
||||
|
||||
freq_word = TextMobject("Frequencies")
|
||||
freq_word.scale(1.5)
|
||||
freq_word.set_color(YELLOW)
|
||||
freq_word.next_to(freq_numbers, DOWN, MED_LARGE_BUFF)
|
||||
|
||||
self.play(
|
||||
LaggedStartMap(
|
||||
FadeInFromDown, freq_numbers
|
||||
)
|
||||
)
|
||||
self.play(
|
||||
Write(freq_word),
|
||||
LaggedStartMap(
|
||||
ShowCreationThenFadeAround, freq_numbers,
|
||||
)
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
self.freq_numbers = freq_numbers
|
||||
self.freq_word = freq_word
|
||||
|
||||
def show_examples_for_frequencies(self):
|
||||
top_circles = self.top_circles
|
||||
c1, c2, c3 = [
|
||||
list(filter(
|
||||
lambda c: c.freq == k,
|
||||
top_circles
|
||||
))[0]
|
||||
for k in (1, 2, 3)
|
||||
]
|
||||
|
||||
neg_circles = VGroup(*filter(
|
||||
lambda c: c.freq < 0,
|
||||
top_circles
|
||||
))
|
||||
|
||||
for c in [c1, c2, c3, *neg_circles]:
|
||||
c.rect = SurroundingRectangle(c)
|
||||
|
||||
self.play(
|
||||
ShowCreation(c2.rect),
|
||||
WiggleOutThenIn(c2.number),
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
ReplacementTransform(c2.rect, c1.rect),
|
||||
)
|
||||
self.play(FadeOut(c1.rect))
|
||||
self.wait()
|
||||
self.play(
|
||||
ShowCreation(c3.rect),
|
||||
WiggleOutThenIn(c3.number),
|
||||
)
|
||||
self.play(
|
||||
FadeOut(c3.rect),
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
LaggedStart(*[
|
||||
ShowCreationThenFadeOut(c.rect)
|
||||
for c in neg_circles
|
||||
])
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(FadeOut(self.freq_word))
|
||||
|
||||
def show_as_vectors(self):
|
||||
top_circles = self.top_circles
|
||||
top_vectors = self.get_rotating_vectors(top_circles)
|
||||
top_vectors.set_color(WHITE)
|
||||
|
||||
original_circles = top_circles.copy()
|
||||
self.play(
|
||||
FadeIn(top_vectors),
|
||||
top_circles.set_opacity, 0,
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(
|
||||
top_circles.match_style, original_circles
|
||||
)
|
||||
self.remove(top_vectors)
|
||||
|
||||
self.top_vectors = top_vectors
|
||||
|
||||
def show_vector_sum(self):
|
||||
trackers = self.center_trackers.copy()
|
||||
trackers.sort(
|
||||
submob_func=lambda t: abs(t.circle.freq - 0.1)
|
||||
)
|
||||
plane = self.plane = NumberPlane(
|
||||
x_min=-3,
|
||||
x_max=3,
|
||||
y_min=-2,
|
||||
y_max=2,
|
||||
axis_config={
|
||||
"stroke_color": LIGHT_GREY,
|
||||
}
|
||||
)
|
||||
plane.set_stroke(width=1)
|
||||
plane.fade(0.5)
|
||||
plane.move_to(self.center_point)
|
||||
|
||||
self.play(
|
||||
FadeOut(self.drawn_path),
|
||||
FadeOut(self.circles),
|
||||
self.slow_factor_tracker.set_value, 0.05,
|
||||
)
|
||||
self.add(plane, self.path)
|
||||
self.play(FadeIn(plane))
|
||||
|
||||
new_circles = VGroup()
|
||||
last_tracker = None
|
||||
for tracker in trackers:
|
||||
if last_tracker:
|
||||
tracker.new_location_func = last_tracker.circle.get_start
|
||||
else:
|
||||
tracker.new_location_func = lambda: self.center_point
|
||||
|
||||
original_circle = tracker.circle
|
||||
tracker.circle = original_circle.copy()
|
||||
tracker.circle.center_func = tracker.get_location
|
||||
new_circles.add(tracker.circle)
|
||||
|
||||
self.add(tracker, tracker.circle)
|
||||
start_point = tracker.get_location()
|
||||
self.play(
|
||||
UpdateFromAlphaFunc(
|
||||
tracker, lambda t, a: t.move_to(
|
||||
interpolate(
|
||||
start_point,
|
||||
tracker.new_location_func(),
|
||||
a,
|
||||
)
|
||||
),
|
||||
run_time=2
|
||||
)
|
||||
)
|
||||
tracker.add_updater(lambda t: t.move_to(
|
||||
t.new_location_func()
|
||||
))
|
||||
self.wait(2)
|
||||
last_tracker = tracker
|
||||
|
||||
self.wait(3)
|
||||
|
||||
self.clear()
|
||||
self.slow_factor_tracker.set_value(0.1)
|
||||
self.add(
|
||||
self.top_circles,
|
||||
self.freq_numbers,
|
||||
self.path,
|
||||
)
|
||||
self.add_circles()
|
||||
for tc in self.top_circles:
|
||||
for c in self.circles:
|
||||
if c.freq == tc.freq:
|
||||
tc.rotate(
|
||||
angle_of_vector(c.get_start() - c.get_center()) -
|
||||
angle_of_vector(tc.get_start() - tc.get_center())
|
||||
)
|
||||
self.wait(10)
|
||||
|
||||
def tweak_starting_vectors(self):
|
||||
top_circles = self.top_circles
|
||||
circles = self.circles
|
||||
path = self.path
|
||||
drawn_path = self.drawn_path
|
||||
|
||||
new_path = self.get_new_path()
|
||||
new_coefs = self.get_coefficients_of_path(new_path)
|
||||
new_circles = self.get_circles(coefficients=new_coefs)
|
||||
|
||||
new_top_circles = VGroup()
|
||||
new_top_vectors = VGroup()
|
||||
for top_circle in top_circles:
|
||||
for circle in new_circles:
|
||||
if circle.freq == top_circle.freq:
|
||||
new_top_circle = circle.copy()
|
||||
new_top_circle.center_func = top_circle.get_center
|
||||
new_top_vector = self.get_rotating_vector(
|
||||
new_top_circle
|
||||
)
|
||||
new_top_circles.add(new_top_circle)
|
||||
new_top_vectors.add(new_top_vector)
|
||||
|
||||
self.play(
|
||||
self.slow_factor_tracker.set_value, 0,
|
||||
FadeOut(drawn_path)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
ReplacementTransform(top_circles, new_top_circles),
|
||||
ReplacementTransform(circles, new_circles),
|
||||
FadeOut(path),
|
||||
run_time=3,
|
||||
)
|
||||
new_drawn_path = self.get_drawn_path(
|
||||
new_circles, stroke_width=4,
|
||||
)
|
||||
self.add(new_drawn_path)
|
||||
self.slow_factor_tracker.set_value(0.1)
|
||||
self.wait(20)
|
||||
|
||||
#
|
||||
def configure_path(self, path):
|
||||
path.set_stroke(WHITE, 1)
|
||||
path.set_fill(BLACK, opacity=1)
|
||||
path.set_height(self.path_height)
|
||||
path.move_to(self.center_point)
|
||||
return path
|
||||
|
||||
def get_path(self):
|
||||
tex = TexMobject("f")
|
||||
path = tex.family_members_with_points()[0]
|
||||
self.configure_path(path)
|
||||
return path
|
||||
# return Square().set_height(3)
|
||||
|
||||
def get_new_path(self):
|
||||
shape = SVGMobject("TrebleClef")
|
||||
path = shape.family_members_with_points()[0]
|
||||
self.configure_path(path)
|
||||
path.scale(1.5, about_edge=DOWN)
|
||||
return path
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,142 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part2.wordy_scenes import WriteHeatEquationTemplate
|
||||
|
||||
|
||||
class ReactionsToInitialHeatEquation(PiCreatureScene):
|
||||
def construct(self):
|
||||
randy = self.pi_creature
|
||||
randy.set_color(BLUE_C)
|
||||
randy.center()
|
||||
|
||||
point = VectorizedPoint().next_to(randy, UL, LARGE_BUFF)
|
||||
randy.add_updater(lambda r: r.look_at(point))
|
||||
|
||||
self.play(randy.change, "horrified")
|
||||
self.wait()
|
||||
self.play(randy.change, "pondering")
|
||||
self.wait()
|
||||
self.play(
|
||||
randy.change, "confused",
|
||||
point.next_to, randy, UR, LARGE_BUFF,
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
point.shift, 2 * DOWN,
|
||||
randy.change, "horrified"
|
||||
)
|
||||
self.wait(4)
|
||||
|
||||
|
||||
class ContrastPDEToODE(TeacherStudentsScene):
|
||||
CONFIG = {
|
||||
"random_seed": 2,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
student = self.students[2]
|
||||
pde, ode = words = VGroup(*[
|
||||
TextMobject(
|
||||
text + "\\\\",
|
||||
"Differential\\\\",
|
||||
"Equation"
|
||||
)
|
||||
for text in ("Partial", "Ordinary")
|
||||
])
|
||||
pde[0].set_color(YELLOW)
|
||||
ode[0].set_color(BLUE)
|
||||
for word in words:
|
||||
word.arrange(DOWN, aligned_edge=LEFT)
|
||||
|
||||
words.arrange(RIGHT, buff=LARGE_BUFF)
|
||||
words.next_to(student.get_corner(UR), UP, MED_LARGE_BUFF)
|
||||
words.shift(UR)
|
||||
lt = TexMobject("<")
|
||||
lt.scale(1.5)
|
||||
lt.move_to(Line(pde.get_right(), ode.get_left()))
|
||||
|
||||
for pi in self.pi_creatures:
|
||||
pi.add_updater(lambda p: p.look_at(pde))
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(VGroup(words, lt)),
|
||||
student.change, "raise_right_hand",
|
||||
)
|
||||
self.play(
|
||||
self.get_student_changes("pondering", "pondering", "hooray"),
|
||||
self.teacher.change, "happy"
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(
|
||||
Swap(ode, pde),
|
||||
self.teacher.change, "raise_right_hand",
|
||||
self.get_student_changes(
|
||||
"erm", "sassy", "confused"
|
||||
)
|
||||
)
|
||||
self.look_at(words)
|
||||
self.change_student_modes(
|
||||
"thinking", "thinking", "tease",
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class AskAboutWhereEquationComesFrom(TeacherStudentsScene, WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
equation = self.get_d1_equation()
|
||||
equation.move_to(self.hold_up_spot, DOWN)
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(equation),
|
||||
self.teacher.change, "raise_right_hand"
|
||||
)
|
||||
self.student_says(
|
||||
"Um...why?",
|
||||
target_mode="sassy",
|
||||
student_index=2,
|
||||
bubble_kwargs={"direction": RIGHT},
|
||||
)
|
||||
self.change_student_modes(
|
||||
"confused", "confused", "sassy",
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
self.teacher.change, "pondering",
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
|
||||
class AskWhyRewriteIt(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.student_says(
|
||||
"Why?", student_index=1,
|
||||
bubble_kwargs={"height": 2, "width": 2},
|
||||
)
|
||||
self.students[1].bubble = None
|
||||
self.teacher_says(
|
||||
"One step closer\\\\to derivatives"
|
||||
)
|
||||
self.change_student_modes(
|
||||
"thinking", "thinking", "thinking",
|
||||
look_at_arg=4 * LEFT + 2 * UP
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
|
||||
class ReferenceKhanVideo(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
khan_logo = ImageMobject("KhanLogo")
|
||||
khan_logo.set_height(1)
|
||||
khan_logo.next_to(self.teacher, UP, buff=2)
|
||||
khan_logo.shift(2 * LEFT)
|
||||
|
||||
self.play(
|
||||
self.teacher.change, "raise_right_hand",
|
||||
)
|
||||
self.change_student_modes(
|
||||
"thinking", "pondering", "thinking",
|
||||
look_at_arg=self.screen
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeInFromDown(khan_logo))
|
||||
self.look_at(self.screen)
|
||||
self.wait(15)
|
||||
@@ -1,35 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
TIME_COLOR = YELLOW
|
||||
X_COLOR = GREEN
|
||||
|
||||
|
||||
def get_heat_equation():
|
||||
pass
|
||||
|
||||
|
||||
def temperature_to_color(temp, min_temp=-1, max_temp=1):
|
||||
colors = [BLUE, TEAL, GREEN, YELLOW, "#ff0000"]
|
||||
|
||||
alpha = inverse_interpolate(min_temp, max_temp, temp)
|
||||
index, sub_alpha = integer_interpolate(
|
||||
0, len(colors) - 1, alpha
|
||||
)
|
||||
return interpolate_color(
|
||||
colors[index], colors[index + 1], sub_alpha
|
||||
)
|
||||
|
||||
|
||||
def two_d_temp_func(x, y, t):
|
||||
return np.sum([
|
||||
c * np.sin(f * var) * np.exp(-(f**2) * t)
|
||||
for c, f, var in [
|
||||
(0.2, 1, x),
|
||||
(0.3, 3, x),
|
||||
(0.02, 5, x),
|
||||
(0.01, 7, x),
|
||||
(0.5, 2, y),
|
||||
(0.1, 10, y),
|
||||
(0.01, 20, y),
|
||||
]
|
||||
])
|
||||
@@ -1,794 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.staging import TourOfDifferentialEquations
|
||||
|
||||
|
||||
class PartTwoOfTour(TourOfDifferentialEquations):
|
||||
CONFIG = {
|
||||
"zoomed_thumbnail_index": 1,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_title()
|
||||
self.show_thumbnails()
|
||||
self.zoom_in_to_one_thumbnail()
|
||||
|
||||
def zoom_in_to_one_thumbnail(self):
|
||||
frame = self.camera_frame
|
||||
thumbnails = self.thumbnails
|
||||
|
||||
ode = TextMobject("Ordinary\\\\", "Differential Equation")
|
||||
pde = TextMobject("Partial\\\\", "Differential Equation")
|
||||
for word, thumbnail, vect in zip([ode, pde], thumbnails, [DOWN, UP]):
|
||||
word.match_width(thumbnail)
|
||||
word.next_to(thumbnail, vect)
|
||||
ode[0].set_color(BLUE)
|
||||
pde[0].set_color(YELLOW)
|
||||
|
||||
self.add(ode)
|
||||
|
||||
frame.save_state()
|
||||
self.play(
|
||||
frame.replace,
|
||||
thumbnails[0],
|
||||
run_time=1,
|
||||
)
|
||||
self.play(
|
||||
Restore(frame, run_time=3),
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(ode, pde),
|
||||
)
|
||||
self.play(
|
||||
ApplyMethod(
|
||||
frame.replace, thumbnails[1],
|
||||
path_arc=(-30 * DEGREES),
|
||||
run_time=3
|
||||
),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class BrownianMotion(Scene):
|
||||
CONFIG = {
|
||||
"wait_time": 60,
|
||||
"L": 3, # Box in [-L, L] x [-L, L]
|
||||
"n_particles": 100,
|
||||
"m1": 1,
|
||||
"m2": 100,
|
||||
"r1": 0.05,
|
||||
"r2": 0.5,
|
||||
"max_v": 5,
|
||||
"random_seed": 2,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_title()
|
||||
self.add_particles()
|
||||
self.wait(self.wait_time)
|
||||
|
||||
def add_title(self):
|
||||
square = Square(side_length=2 * self.L)
|
||||
title = TextMobject("Brownian motion")
|
||||
title.scale(1.5)
|
||||
title.next_to(square, UP)
|
||||
|
||||
self.add(square)
|
||||
self.add(title)
|
||||
|
||||
def add_particles(self):
|
||||
m1 = self.m1
|
||||
m2 = self.m2
|
||||
r1 = self.r1
|
||||
r2 = self.r2
|
||||
L = self.L
|
||||
max_v = self.max_v
|
||||
n_particles = self.n_particles
|
||||
|
||||
lil_particles = VGroup(*[
|
||||
self.get_particle(m1, r1, L, max_v)
|
||||
for k in range(n_particles)
|
||||
])
|
||||
big_particle = self.get_particle(m2, r2, L=r2, max_v=0)
|
||||
big_particle.set_fill(YELLOW, 1)
|
||||
|
||||
for p in lil_particles:
|
||||
if self.are_colliding(p, big_particle):
|
||||
lil_particles.remove(p)
|
||||
all_particles = VGroup(big_particle, *lil_particles)
|
||||
all_particles.add_updater(self.update_particles)
|
||||
|
||||
path = self.get_traced_path(big_particle)
|
||||
|
||||
self.add(all_particles)
|
||||
self.add(path)
|
||||
|
||||
self.particles = all_particles
|
||||
self.big_particle = big_particle
|
||||
self.path = path
|
||||
|
||||
def get_particle(self, m, r, L, max_v):
|
||||
dot = Dot(radius=r)
|
||||
dot.set_fill(WHITE, 0.7)
|
||||
dot.mass = m
|
||||
dot.radius = r
|
||||
dot.center = op.add(
|
||||
np.random.uniform(-L + r, L - r) * RIGHT,
|
||||
np.random.uniform(-L + r, L - r) * UP
|
||||
)
|
||||
dot.move_to(dot.center)
|
||||
dot.velocity = rotate_vector(
|
||||
np.random.uniform(0, max_v) * RIGHT,
|
||||
np.random.uniform(0, TAU),
|
||||
)
|
||||
return dot
|
||||
|
||||
def are_colliding(self, p1, p2):
|
||||
d = get_norm(p1.get_center() - p2.get_center())
|
||||
return (d < p1.radius + p2.radius)
|
||||
|
||||
def get_traced_path(self, particle):
|
||||
path = VMobject()
|
||||
path.set_stroke(BLUE, 3)
|
||||
path.start_new_path(particle.get_center())
|
||||
|
||||
buff = 0.02
|
||||
|
||||
def update_path(path):
|
||||
new_point = particle.get_center()
|
||||
if get_norm(new_point - path.get_last_point()) > buff:
|
||||
path.add_line_to(new_point)
|
||||
|
||||
path.add_updater(update_path)
|
||||
return path
|
||||
|
||||
def update_particles(self, particles, dt):
|
||||
for p1 in particles:
|
||||
p1.center += p1.velocity * dt
|
||||
|
||||
# Check particle collisions
|
||||
buff = 0.01
|
||||
for p2 in particles:
|
||||
if p1 is p2:
|
||||
continue
|
||||
v = p2.center - p1.center
|
||||
dist = get_norm(v)
|
||||
r_sum = p1.radius + p2.radius
|
||||
diff = dist - r_sum
|
||||
if diff < 0:
|
||||
unit_v = v / dist
|
||||
p1.center += (diff - buff) * unit_v / 2
|
||||
p2.center += -(diff - buff) * unit_v / 2
|
||||
u1 = p1.velocity
|
||||
u2 = p2.velocity
|
||||
m1 = p1.mass
|
||||
m2 = p2.mass
|
||||
v1 = (
|
||||
(m2 * (u2 - u1) + m1 * u1 + m2 * u2) /
|
||||
(m1 + m2)
|
||||
)
|
||||
v2 = (
|
||||
(m1 * (u1 - u2) + m1 * u1 + m2 * u2) /
|
||||
(m1 + m2)
|
||||
)
|
||||
p1.velocity = v1
|
||||
p2.velocity = v2
|
||||
|
||||
# Check edge collisions
|
||||
r1 = p1.radius
|
||||
c1 = p1.center
|
||||
for i in [0, 1]:
|
||||
if abs(c1[i]) + r1 > self.L:
|
||||
c1[i] = np.sign(c1[i]) * (self.L - r1)
|
||||
p1.velocity[i] *= -1 * op.mul(
|
||||
np.sign(p1.velocity[i]),
|
||||
np.sign(c1[i])
|
||||
)
|
||||
|
||||
for p in particles:
|
||||
p.move_to(p.center)
|
||||
return particles
|
||||
|
||||
|
||||
class AltBrownianMotion(BrownianMotion):
|
||||
CONFIG = {
|
||||
"wait_time": 20,
|
||||
"n_particles": 100,
|
||||
"m2": 10,
|
||||
}
|
||||
|
||||
|
||||
class BlackScholes(AltBrownianMotion):
|
||||
def construct(self):
|
||||
# For some reason I'm amused by the thought
|
||||
# Of this graph perfectly matching the Brownian
|
||||
# Motion y-coordiante
|
||||
self.add_title()
|
||||
self.add_particles()
|
||||
self.particles.set_opacity(0)
|
||||
self.remove(self.path)
|
||||
self.add_graph()
|
||||
self.wait(self.wait_time)
|
||||
|
||||
def add_title(self):
|
||||
title = TextMobject("Black-Sholes equations")
|
||||
title.scale(1.5)
|
||||
title.next_to(2 * UP, UP)
|
||||
|
||||
equation = TexMobject(
|
||||
"{\\partial V \\over \\partial t}", "+",
|
||||
"\\frac{1}{2} \\sigma^2 S^2",
|
||||
"{\\partial^2 V \\over \\partial S^2}", "+",
|
||||
"rS", "{\\partial V \\over \\partial S}",
|
||||
"-rV", "=", "0",
|
||||
)
|
||||
equation.scale(0.8)
|
||||
equation.next_to(title, DOWN)
|
||||
|
||||
self.add(title)
|
||||
self.add(equation)
|
||||
self.title = title
|
||||
self.equation = equation
|
||||
|
||||
def add_graph(self):
|
||||
axes = Axes(
|
||||
x_min=-1,
|
||||
x_max=20,
|
||||
y_min=0,
|
||||
y_max=10,
|
||||
number_line_config={
|
||||
"unit_size": 0.5,
|
||||
},
|
||||
)
|
||||
axes.set_height(4)
|
||||
axes.move_to(DOWN)
|
||||
|
||||
def get_graph_point():
|
||||
return axes.c2p(
|
||||
self.get_time(),
|
||||
5 + 2 * self.big_particle.get_center()[1]
|
||||
)
|
||||
|
||||
graph = VMobject()
|
||||
graph.match_style(self.path)
|
||||
graph.start_new_path(get_graph_point())
|
||||
graph.add_updater(
|
||||
lambda g: g.add_line_to(get_graph_point())
|
||||
)
|
||||
|
||||
self.add(axes)
|
||||
self.add(graph)
|
||||
|
||||
|
||||
class ContrastChapters1And2(Scene):
|
||||
def construct(self):
|
||||
c1_frame, c2_frame = frames = VGroup(*[
|
||||
ScreenRectangle(height=3.5)
|
||||
for x in range(2)
|
||||
])
|
||||
frames.arrange(RIGHT, buff=LARGE_BUFF)
|
||||
|
||||
c1_title, c2_title = titles = VGroup(
|
||||
TextMobject("Chapter 1"),
|
||||
TextMobject("Chapter 2"),
|
||||
)
|
||||
titles.scale(1.5)
|
||||
|
||||
ode, pde = des = VGroup(
|
||||
TextMobject(
|
||||
"Ordinary",
|
||||
"Differential Equations\\\\",
|
||||
"ODEs",
|
||||
),
|
||||
TextMobject(
|
||||
"Partial",
|
||||
"Differential Equations\\\\",
|
||||
"PDEs",
|
||||
),
|
||||
)
|
||||
ode[0].set_color(BLUE)
|
||||
pde[0].set_color(YELLOW)
|
||||
for de in des:
|
||||
de[-1][0].match_color(de[0])
|
||||
de[-1].scale(1.5, about_point=de.get_top())
|
||||
|
||||
for title, frame, de in zip(titles, frames, des):
|
||||
title.next_to(frame, UP)
|
||||
de.match_width(frame)
|
||||
de.next_to(frame, DOWN)
|
||||
|
||||
lt = TexMobject("<")
|
||||
lt.move_to(Line(ode.get_right(), pde.get_left()))
|
||||
lt.scale(2, about_edge=UP)
|
||||
|
||||
c1_words = TextMobject(
|
||||
"They're", "really\\\\", "{}",
|
||||
"freaking", "hard\\\\",
|
||||
"to", "solve\\\\",
|
||||
)
|
||||
c1_words.set_height(0.5 * c1_frame.get_height())
|
||||
c1_words.move_to(c1_frame)
|
||||
|
||||
c2_words = TextMobject(
|
||||
"They're", "really", "\\emph{really}\\\\",
|
||||
"freaking", "hard\\\\",
|
||||
"to", "solve\\\\",
|
||||
)
|
||||
c2_words.set_color_by_tex("\\emph", YELLOW)
|
||||
c2_words.move_to(c2_frame)
|
||||
edit_shift = MED_LARGE_BUFF * RIGHT
|
||||
c2_edits = VGroup(
|
||||
TextMobject("sometimes").next_to(
|
||||
c2_words[1:3], UP,
|
||||
aligned_edge=LEFT,
|
||||
),
|
||||
Line(
|
||||
c2_words[1].get_left(),
|
||||
c2_words[2].get_right(),
|
||||
stroke_width=8,
|
||||
),
|
||||
TextMobject("not too").next_to(
|
||||
c2_words[3], LEFT,
|
||||
),
|
||||
Line(
|
||||
c2_words[3].get_left(),
|
||||
c2_words[3].get_right(),
|
||||
stroke_width=8,
|
||||
),
|
||||
)
|
||||
c2_edits.set_color(RED)
|
||||
c2_edits[2:].shift(edit_shift)
|
||||
|
||||
self.add(titles)
|
||||
self.add(frames)
|
||||
self.add(des)
|
||||
|
||||
self.wait()
|
||||
self.play(LaggedStartMap(
|
||||
FadeInFromDown, c1_words,
|
||||
lag_ratio=0.1,
|
||||
))
|
||||
self.wait()
|
||||
# self.play(FadeIn(ode))
|
||||
self.play(
|
||||
# TransformFromCopy(ode, pde),
|
||||
TransformFromCopy(c1_words, c2_words),
|
||||
Write(lt)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(c2_edits[:2], run_time=1),
|
||||
)
|
||||
self.play(
|
||||
c2_words[3:5].shift, edit_shift,
|
||||
Write(c2_edits[2:]),
|
||||
run_time=1,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ShowCubeFormation(ThreeDScene):
|
||||
CONFIG = {
|
||||
"camera_config": {
|
||||
"shading_factor": 1.0,
|
||||
},
|
||||
"color": False,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
light_source = self.camera.light_source
|
||||
light_source.move_to(np.array([-6, -3, 6]))
|
||||
|
||||
cube = Cube(
|
||||
side_length=4,
|
||||
fill_color=GREY,
|
||||
stroke_color=WHITE,
|
||||
stroke_width=0.5,
|
||||
)
|
||||
cube.set_fill(opacity=1)
|
||||
if self.color:
|
||||
# cube[0].set_color(BLUE)
|
||||
# cube[1].set_color(RED)
|
||||
# for face in cube[2:]:
|
||||
# face.set_color([BLUE, RED])
|
||||
cube.color_using_background_image("VerticalTempGradient")
|
||||
|
||||
# light_source.next_to(cube, np.array([1, -1, 1]), buff=2)
|
||||
|
||||
cube_3d = cube.copy()
|
||||
cube_2d = cube_3d.copy().stretch(0, 2)
|
||||
cube_1d = cube_2d.copy().stretch(0, 1)
|
||||
cube_0d = cube_1d.copy().stretch(0, 0)
|
||||
|
||||
cube.become(cube_0d)
|
||||
|
||||
self.set_camera_orientation(
|
||||
phi=70 * DEGREES,
|
||||
theta=-145 * DEGREES,
|
||||
)
|
||||
self.begin_ambient_camera_rotation(rate=0.05)
|
||||
|
||||
for target in [cube_1d, cube_2d, cube_3d]:
|
||||
self.play(
|
||||
Transform(cube, target, run_time=1.5)
|
||||
)
|
||||
self.wait(8)
|
||||
|
||||
|
||||
class ShowCubeFormationWithColor(ShowCubeFormation):
|
||||
CONFIG = {
|
||||
"color": True,
|
||||
}
|
||||
|
||||
|
||||
class ShowRect(Scene):
|
||||
CONFIG = {
|
||||
"height": 1,
|
||||
"width": 3,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
rect = Rectangle(
|
||||
height=self.height,
|
||||
width=self.width,
|
||||
)
|
||||
rect.set_color(YELLOW)
|
||||
self.play(ShowCreationThenFadeOut(rect))
|
||||
|
||||
|
||||
class ShowSquare(ShowRect):
|
||||
CONFIG = {
|
||||
"height": 1,
|
||||
"width": 1,
|
||||
}
|
||||
|
||||
|
||||
class ShowHLine(Scene):
|
||||
def construct(self):
|
||||
line = Line(LEFT, RIGHT)
|
||||
line.set_color(BLUE)
|
||||
self.play(ShowCreationThenFadeOut(line))
|
||||
|
||||
|
||||
class ShowCross(Scene):
|
||||
def construct(self):
|
||||
cross = Cross(Square())
|
||||
cross.set_width(3)
|
||||
cross.set_height(1, stretch=True)
|
||||
self.play(ShowCreation(cross))
|
||||
|
||||
|
||||
class TwoBodyEquations(Scene):
|
||||
def construct(self):
|
||||
kw = {
|
||||
"tex_to_color_map": {
|
||||
"x_1": LIGHT_GREY,
|
||||
"y_1": LIGHT_GREY,
|
||||
"x_2": BLUE,
|
||||
"y_2": BLUE,
|
||||
"=": WHITE,
|
||||
}
|
||||
}
|
||||
equations = VGroup(
|
||||
TexMobject(
|
||||
"{d^2 x_1 \\over dt^2}",
|
||||
"=",
|
||||
"{x_2 - x_1 \\over m_1 \\left(",
|
||||
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
|
||||
"\\right)^{3/2}",
|
||||
**kw
|
||||
),
|
||||
TexMobject(
|
||||
"{d^2 y_1 \\over dt^2}",
|
||||
"=",
|
||||
"{y_2 - y_1 \\over m_1 \\left(",
|
||||
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
|
||||
"\\right)^{3/2}",
|
||||
**kw
|
||||
),
|
||||
TexMobject(
|
||||
"{d^2 x_2 \\over dt^2}",
|
||||
"=",
|
||||
"{x_1 - x_2 \\over m_2 \\left(",
|
||||
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
|
||||
"\\right)^{3/2}",
|
||||
**kw
|
||||
),
|
||||
TexMobject(
|
||||
"{d^2 y_2 \\over dt^2}",
|
||||
"=",
|
||||
"{y_1 - y_2 \\over m_2 \\left(",
|
||||
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
|
||||
"\\right)^{3/2}",
|
||||
**kw
|
||||
),
|
||||
)
|
||||
|
||||
equations.arrange(DOWN, buff=LARGE_BUFF)
|
||||
equations.set_height(6)
|
||||
equations.to_edge(LEFT)
|
||||
|
||||
variables = VGroup()
|
||||
lhss = VGroup()
|
||||
rhss = VGroup()
|
||||
for equation in equations:
|
||||
variable = equation[1]
|
||||
lhs = equation[:4]
|
||||
rhs = equation[4:]
|
||||
variables.add(variable)
|
||||
lhss.add(lhs)
|
||||
rhss.add(rhs)
|
||||
lhss_copy = lhss.copy()
|
||||
|
||||
for variable, lhs in zip(variables, lhss):
|
||||
variable.save_state()
|
||||
variable.match_height(lhs)
|
||||
variable.scale(0.7)
|
||||
variable.move_to(lhs, LEFT)
|
||||
|
||||
self.play(LaggedStart(*[
|
||||
FadeInFrom(v, RIGHT)
|
||||
for v in variables
|
||||
]))
|
||||
self.wait()
|
||||
self.play(
|
||||
LaggedStartMap(Restore, variables),
|
||||
FadeIn(
|
||||
lhss_copy,
|
||||
remover=True,
|
||||
lag_ratio=0.1,
|
||||
run_time=2,
|
||||
)
|
||||
)
|
||||
self.add(lhss)
|
||||
self.wait()
|
||||
self.play(LaggedStartMap(
|
||||
FadeIn, rhss
|
||||
))
|
||||
self.wait()
|
||||
self.play(
|
||||
LaggedStart(*[
|
||||
ShowCreationThenFadeAround(lhs[:3])
|
||||
for lhs in lhss
|
||||
])
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
LaggedStartMap(
|
||||
ShowCreationThenFadeAround,
|
||||
rhss,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class LaplacianIntuition(SpecialThreeDScene):
|
||||
CONFIG = {
|
||||
"three_d_axes_config": {
|
||||
"x_min": -5,
|
||||
"x_max": 5,
|
||||
"y_min": -5,
|
||||
"y_max": 5,
|
||||
},
|
||||
"surface_resolution": 32,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
axes = self.get_axes()
|
||||
axes.scale(0.5, about_point=ORIGIN)
|
||||
self.set_camera_to_default_position()
|
||||
self.begin_ambient_camera_rotation()
|
||||
|
||||
def func(x, y):
|
||||
return np.array([
|
||||
x, y,
|
||||
2.7 + 0.5 * (np.sin(x) + np.cos(y)) -
|
||||
0.025 * (x**2 + y**2)
|
||||
])
|
||||
|
||||
surface_config = {
|
||||
"u_min": -5,
|
||||
"u_max": 5,
|
||||
"v_min": -5,
|
||||
"v_max": 5,
|
||||
"resolution": self.surface_resolution,
|
||||
}
|
||||
# plane = ParametricSurface(
|
||||
# lambda u, v: np.array([u, v, 0]),
|
||||
# **surface_config
|
||||
# )
|
||||
# plane.set_stroke(WHITE, width=0.1)
|
||||
# plane.set_fill(WHITE, opacity=0.1)
|
||||
plane = Square(
|
||||
side_length=10,
|
||||
stroke_width=0,
|
||||
fill_color=WHITE,
|
||||
fill_opacity=0.1,
|
||||
)
|
||||
plane.center()
|
||||
plane.set_shade_in_3d(True)
|
||||
|
||||
surface = ParametricSurface(
|
||||
func, **surface_config
|
||||
)
|
||||
surface.set_stroke(BLUE, width=0.1)
|
||||
surface.set_fill(BLUE, opacity=0.25)
|
||||
|
||||
self.add(axes, plane, surface)
|
||||
|
||||
point = VectorizedPoint(np.array([2, -2, 0]))
|
||||
dot = Dot()
|
||||
dot.set_color(GREEN)
|
||||
dot.add_updater(lambda d: d.move_to(point))
|
||||
line = always_redraw(lambda: DashedLine(
|
||||
point.get_location(),
|
||||
func(*point.get_location()[:2]),
|
||||
background_image_file="VerticalTempGradient",
|
||||
))
|
||||
|
||||
circle = Circle(radius=0.25)
|
||||
circle.set_color(YELLOW)
|
||||
circle.insert_n_curves(20)
|
||||
circle.add_updater(lambda m: m.move_to(point))
|
||||
circle.set_shade_in_3d(True)
|
||||
surface_circle = always_redraw(
|
||||
lambda: circle.copy().apply_function(
|
||||
lambda p: func(*p[:2])
|
||||
).shift(
|
||||
0.02 * IN
|
||||
).color_using_background_image("VerticalTempGradient")
|
||||
)
|
||||
|
||||
self.play(FadeInFromLarge(dot))
|
||||
self.play(ShowCreation(line))
|
||||
self.play(TransformFromCopy(dot, circle))
|
||||
self.play(
|
||||
Transform(
|
||||
circle.copy(),
|
||||
surface_circle.copy().clear_updaters(),
|
||||
remover=True,
|
||||
)
|
||||
)
|
||||
self.add(surface_circle)
|
||||
|
||||
self.wait()
|
||||
for vect in [4 * LEFT, DOWN, 4 * RIGHT, UP]:
|
||||
self.play(
|
||||
point.shift, vect,
|
||||
run_time=3,
|
||||
)
|
||||
|
||||
|
||||
class StrogatzMention(PiCreatureScene):
|
||||
def construct(self):
|
||||
self.show_book()
|
||||
self.show_motives()
|
||||
self.show_pages()
|
||||
|
||||
def show_book(self):
|
||||
morty = self.pi_creature
|
||||
book = ImageMobject("InfinitePowers")
|
||||
book.set_height(5)
|
||||
book.to_edge(LEFT)
|
||||
|
||||
steve = ImageMobject("Strogatz_by_bricks")
|
||||
steve.set_height(5)
|
||||
steve.to_edge(LEFT)
|
||||
|
||||
name = TextMobject("Steven Strogatz")
|
||||
name.match_width(steve)
|
||||
name.next_to(steve, DOWN)
|
||||
|
||||
self.think(
|
||||
"Hmm...many good\\\\lessons here...",
|
||||
run_time=1
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeInFromDown(steve))
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeInFrom(book, DOWN),
|
||||
steve.shift, 4 * RIGHT,
|
||||
RemovePiCreatureBubble(
|
||||
morty, target_mode="thinking"
|
||||
)
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(
|
||||
FadeOut(steve),
|
||||
FadeOut(morty),
|
||||
)
|
||||
|
||||
self.book = book
|
||||
|
||||
def show_motives(self):
|
||||
motives = VGroup(
|
||||
TextMobject("1) Scratch and itch"),
|
||||
TextMobject("2) Make people love math"),
|
||||
)
|
||||
motives.scale(1.5)
|
||||
motives.arrange(
|
||||
DOWN, LARGE_BUFF,
|
||||
aligned_edge=LEFT,
|
||||
)
|
||||
motives.move_to(
|
||||
Line(
|
||||
self.book.get_right(),
|
||||
FRAME_WIDTH * RIGHT / 2
|
||||
)
|
||||
)
|
||||
motives.to_edge(UP)
|
||||
|
||||
for motive in motives:
|
||||
self.play(FadeInFromDown(motive))
|
||||
self.wait(2)
|
||||
self.play(FadeOut(motives))
|
||||
|
||||
def show_pages(self):
|
||||
book = self.book
|
||||
pages = Group(*[
|
||||
ImageMobject("IP_Sample_Page{}".format(i))
|
||||
for i in range(1, 4)
|
||||
])
|
||||
for page in pages:
|
||||
page.match_height(book)
|
||||
page.next_to(book, RIGHT)
|
||||
|
||||
last_page = VectorizedPoint()
|
||||
for page in pages:
|
||||
self.play(
|
||||
FadeOut(last_page),
|
||||
FadeIn(page)
|
||||
)
|
||||
self.wait()
|
||||
last_page = page
|
||||
|
||||
self.play(FadeOut(last_page))
|
||||
|
||||
def create_pi_creature(self):
|
||||
return Mortimer().to_corner(DR)
|
||||
|
||||
|
||||
class Thumbnail(Scene):
|
||||
def construct(self):
|
||||
image = ImageMobject("HeatSurfaceExampleFlipped")
|
||||
image.set_height(6.5)
|
||||
image.to_edge(DOWN, buff=-SMALL_BUFF)
|
||||
self.add(image)
|
||||
|
||||
equation = TexMobject(
|
||||
"{\\partial {T} \\over \\partial {t}}", "=",
|
||||
"\\alpha", "\\nabla^2 {T}",
|
||||
tex_to_color_map={
|
||||
"{t}": YELLOW,
|
||||
"{T}": RED,
|
||||
}
|
||||
)
|
||||
equation.scale(2)
|
||||
equation.to_edge(UP)
|
||||
|
||||
self.add(equation)
|
||||
|
||||
Group(equation, image).shift(1.5 * RIGHT)
|
||||
|
||||
question = TextMobject("What is\\\\this?")
|
||||
question.scale(2.5)
|
||||
question.to_edge(LEFT)
|
||||
arrow = Arrow(
|
||||
question.get_top(),
|
||||
equation.get_left(),
|
||||
buff=0.5,
|
||||
path_arc=-90 * DEGREES,
|
||||
)
|
||||
arrow.set_stroke(width=5)
|
||||
|
||||
self.add(question, arrow)
|
||||
|
||||
|
||||
class ShowNewton(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
||||
|
||||
class ShowCupOfWater(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
@@ -1,785 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class WriteHeatEquationTemplate(Scene):
|
||||
CONFIG = {
|
||||
"tex_mobject_config": {
|
||||
"tex_to_color_map": {
|
||||
"{T}": WHITE,
|
||||
"{t}": YELLOW,
|
||||
"{x}": GREEN,
|
||||
"{y}": RED,
|
||||
"{z}": BLUE,
|
||||
"\\partial": WHITE,
|
||||
"2": WHITE,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def get_d1_equation(self):
|
||||
return TexMobject(
|
||||
"{\\partial {T} \\over \\partial {t}}({x}, {t})", "=",
|
||||
"\\alpha \\cdot",
|
||||
"{\\partial^2 {T} \\over \\partial {x}^2} ({x}, {t})",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
|
||||
def get_d1_equation_without_inputs(self):
|
||||
return TexMobject(
|
||||
"{\\partial {T} \\over \\partial {t}}", "=",
|
||||
"\\alpha \\cdot",
|
||||
"{\\partial^2 {T} \\over \\partial {x}^2}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
|
||||
def get_d3_equation(self):
|
||||
return TexMobject(
|
||||
"{\\partial {T} \\over \\partial {t}}", "=",
|
||||
"\\alpha \\left(",
|
||||
"{\\partial^2 {T} \\over \\partial {x}^2} + ",
|
||||
"{\\partial^2 {T} \\over \\partial {y}^2} + ",
|
||||
"{\\partial^2 {T} \\over \\partial {z}^2}",
|
||||
"\\right)",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
|
||||
def get_general_equation(self):
|
||||
return TexMobject(
|
||||
"{\\partial {T} \\over \\partial {t}}", "=",
|
||||
"\\alpha", "\\nabla^2 {T}",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
|
||||
def get_d3_equation_with_inputs(self):
|
||||
return TexMobject(
|
||||
"{\\partial {T} \\over \\partial {t}}",
|
||||
"({x}, {y}, {z}, {t})", "=",
|
||||
"\\alpha \\left(",
|
||||
"{\\partial^2 {T} \\over \\partial {x}^2}",
|
||||
"({x}, {y}, {z}, {t}) + ",
|
||||
"{\\partial^2 {T} \\over \\partial {y}^2}",
|
||||
"({x}, {y}, {z}, {t}) + ",
|
||||
"{\\partial^2 {T} \\over \\partial {z}^2}",
|
||||
"({x}, {y}, {z}, {t})",
|
||||
"\\right)",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
|
||||
def get_d1_words(self):
|
||||
return TextMobject("Heat equation\\\\", "(1 dimension)")
|
||||
|
||||
def get_d3_words(self):
|
||||
return TextMobject("Heat equation\\\\", "(3 dimensions)")
|
||||
|
||||
def get_d1_group(self):
|
||||
group = VGroup(
|
||||
self.get_d1_words(),
|
||||
self.get_d1_equation(),
|
||||
)
|
||||
group.arrange(DOWN, buff=MED_LARGE_BUFF)
|
||||
return group
|
||||
|
||||
def get_d3_group(self):
|
||||
group = VGroup(
|
||||
self.get_d3_words(),
|
||||
self.get_d3_equation(),
|
||||
)
|
||||
group.arrange(DOWN, buff=MED_LARGE_BUFF)
|
||||
return group
|
||||
|
||||
|
||||
class HeatEquationIntroTitle(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
scale_factor = 1.25
|
||||
title = TextMobject("The Heat Equation")
|
||||
title.scale(scale_factor)
|
||||
title.to_edge(UP)
|
||||
|
||||
equation = self.get_general_equation()
|
||||
equation.scale(scale_factor)
|
||||
equation.next_to(title, DOWN, MED_LARGE_BUFF)
|
||||
equation.set_color_by_tex("{T}", RED)
|
||||
|
||||
self.play(
|
||||
FadeInFrom(title, DOWN),
|
||||
FadeInFrom(equation, UP),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class BringTogether(Scene):
|
||||
def construct(self):
|
||||
arrows = VGroup(Vector(2 * RIGHT), Vector(2 * LEFT))
|
||||
arrows.arrange(RIGHT, buff=2)
|
||||
words = TextMobject("Bring together")[0]
|
||||
words.next_to(arrows, DOWN)
|
||||
words.save_state()
|
||||
words.space_out_submobjects(1.2)
|
||||
|
||||
self.play(
|
||||
VFadeIn(words),
|
||||
Restore(words),
|
||||
arrows.arrange, RIGHT, {"buff": SMALL_BUFF},
|
||||
VFadeIn(arrows),
|
||||
)
|
||||
self.play(FadeOut(words), FadeOut(arrows))
|
||||
|
||||
|
||||
class FourierSeriesIntro(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
title_scale_value = 1.5
|
||||
|
||||
title = TextMobject(
|
||||
"Fourier ", "Series",
|
||||
)
|
||||
title.scale(title_scale_value)
|
||||
title.to_edge(UP)
|
||||
title.generate_target()
|
||||
|
||||
details_coming = TextMobject("Details coming...")
|
||||
details_coming.next_to(title.get_corner(DR), DOWN)
|
||||
details_coming.set_color(LIGHT_GREY)
|
||||
|
||||
# physics = TextMobject("Physics")
|
||||
heat = TextMobject("Heat")
|
||||
heat.scale(title_scale_value)
|
||||
physics = self.get_general_equation()
|
||||
physics.set_color_by_tex("{T}", RED)
|
||||
arrow1 = Arrow(LEFT, RIGHT)
|
||||
arrow2 = Arrow(LEFT, RIGHT)
|
||||
group = VGroup(
|
||||
heat, arrow1, physics, arrow2, title.target
|
||||
)
|
||||
group.arrange(RIGHT)
|
||||
# physics.align_to(title.target, UP)
|
||||
group.to_edge(UP)
|
||||
|
||||
rot_square = Square()
|
||||
rot_square.fade(1)
|
||||
rot_square.add_updater(lambda m, dt: m.rotate(dt))
|
||||
|
||||
def update_heat_colors(heat):
|
||||
colors = [YELLOW, RED]
|
||||
vertices = rot_square.get_vertices()
|
||||
letters = heat.family_members_with_points()
|
||||
for letter, vertex in zip(letters, vertices):
|
||||
alpha = (normalize(vertex)[0] + 1) / 2
|
||||
i, sa = integer_interpolate(0, len(colors) - 1, alpha)
|
||||
letter.set_color(interpolate_color(
|
||||
colors[i], colors[i + 1], alpha,
|
||||
))
|
||||
heat.add_updater(update_heat_colors)
|
||||
|
||||
image = ImageMobject("Joseph Fourier")
|
||||
image.set_height(5)
|
||||
image.next_to(title, DOWN, LARGE_BUFF)
|
||||
image.to_edge(LEFT)
|
||||
name = TextMobject("Joseph", "Fourier")
|
||||
name.next_to(image, DOWN)
|
||||
|
||||
bubble = ThoughtBubble(
|
||||
height=2,
|
||||
width=2.5,
|
||||
direction=RIGHT,
|
||||
)
|
||||
bubble.set_fill(opacity=0)
|
||||
bubble.set_stroke(WHITE)
|
||||
bubble.set_stroke(BLACK, 5, background=True)
|
||||
bubble.shift(heat.get_center() - bubble.get_bubble_center())
|
||||
bubble[:-1].shift(LEFT + 0.2 * DOWN)
|
||||
bubble[:-1].rotate(-20 * DEGREES)
|
||||
for mob in bubble[:-1]:
|
||||
mob.rotate(20 * DEGREES)
|
||||
|
||||
# self.play(FadeInFromDown(title))
|
||||
self.add(title)
|
||||
self.play(
|
||||
FadeInFromDown(image),
|
||||
TransformFromCopy(
|
||||
title.get_part_by_tex("Fourier"),
|
||||
name.get_part_by_tex("Fourier"),
|
||||
path_arc=90 * DEGREES,
|
||||
),
|
||||
FadeIn(name.get_part_by_tex("Joseph")),
|
||||
)
|
||||
self.play(Write(details_coming, run_time=1))
|
||||
self.play(LaggedStartMap(FadeOut, details_coming[0], run_time=1))
|
||||
self.wait()
|
||||
self.add(rot_square)
|
||||
self.play(
|
||||
FadeInFrom(physics, RIGHT),
|
||||
GrowArrow(arrow2),
|
||||
FadeInFrom(heat, RIGHT),
|
||||
GrowArrow(arrow1),
|
||||
MoveToTarget(title),
|
||||
)
|
||||
self.play(ShowCreation(bubble))
|
||||
self.wait(10)
|
||||
|
||||
|
||||
class CompareODEToPDE(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
||||
|
||||
class TodaysTargetWrapper(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
||||
|
||||
class TwoGraphTypeTitles(Scene):
|
||||
def construct(self):
|
||||
left_title = TextMobject(
|
||||
"Represent time\\\\with actual time"
|
||||
)
|
||||
left_title.shift(FRAME_WIDTH * LEFT / 4)
|
||||
right_title = TextMobject(
|
||||
"Represent time\\\\with an axis"
|
||||
)
|
||||
right_title.shift(FRAME_WIDTH * RIGHT / 4)
|
||||
|
||||
titles = VGroup(left_title, right_title)
|
||||
for title in titles:
|
||||
title.scale(1.25)
|
||||
title.to_edge(UP)
|
||||
|
||||
self.play(FadeInFromDown(right_title))
|
||||
self.wait()
|
||||
self.play(FadeInFromDown(left_title))
|
||||
self.wait()
|
||||
|
||||
|
||||
class ShowPartialDerivativeSymbols(Scene):
|
||||
def construct(self):
|
||||
t2c = {
|
||||
"{x}": GREEN,
|
||||
"{t}": YELLOW,
|
||||
}
|
||||
d_derivs, del_derivs = VGroup(*[
|
||||
VGroup(*[
|
||||
TexMobject(
|
||||
"{" + sym, "T", "\\over", sym, var + "}",
|
||||
"(", "{x}", ",", "{t}", ")",
|
||||
).set_color_by_tex_to_color_map(t2c)
|
||||
for var in ("{x}", "{t}")
|
||||
])
|
||||
for sym in ("d", "\\partial")
|
||||
])
|
||||
dTdx, dTdt = d_derivs
|
||||
delTdelx, delTdelx = del_derivs
|
||||
dels = VGroup(*it.chain(*[
|
||||
del_deriv.get_parts_by_tex("\\partial")
|
||||
for del_deriv in del_derivs
|
||||
]))
|
||||
|
||||
dTdx.to_edge(UP)
|
||||
self.play(FadeInFrom(dTdx, DOWN))
|
||||
self.wait()
|
||||
self.play(ShowCreationThenFadeAround(dTdx[3:5]))
|
||||
self.play(ShowCreationThenFadeAround(dTdx[:2]))
|
||||
self.wait()
|
||||
|
||||
dTdt.move_to(dTdx)
|
||||
self.play(
|
||||
dTdx.next_to, dTdt, RIGHT, {"buff": 1.5},
|
||||
dTdx.set_opacity, 0.5,
|
||||
FadeInFromDown(dTdt)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
for m1, m2 in zip(d_derivs, del_derivs):
|
||||
m2.move_to(m1)
|
||||
|
||||
pd_words = TextMobject("Partial derivatives")
|
||||
pd_words.next_to(del_derivs, DOWN, MED_LARGE_BUFF)
|
||||
|
||||
self.play(
|
||||
Write(pd_words),
|
||||
dTdx.set_opacity, 1,
|
||||
run_time=1,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
ReplacementTransform(d_derivs, del_derivs)
|
||||
)
|
||||
self.play(
|
||||
LaggedStartMap(
|
||||
ShowCreationThenFadeAround,
|
||||
dels,
|
||||
surrounding_rectangle_config={
|
||||
"color": BLUE,
|
||||
"buff": 0.5 * SMALL_BUFF,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
num_words = VGroup(*[
|
||||
TextMobject(
|
||||
"Change in $T$\\\\caused by {}",
|
||||
"$\\partial$", "${}$".format(var),
|
||||
arg_separator="",
|
||||
).set_color_by_tex_to_color_map(t2c)
|
||||
for var in ("{x}", "{t}")
|
||||
])
|
||||
num_words.scale(0.8)
|
||||
for word, deriv in zip(num_words, del_derivs):
|
||||
num = deriv[:2]
|
||||
word.move_to(num, UP)
|
||||
word.to_edge(UP, buff=MED_SMALL_BUFF)
|
||||
deriv.rect = SurroundingRectangle(
|
||||
num,
|
||||
buff=SMALL_BUFF,
|
||||
stroke_width=2,
|
||||
color=word[-1].get_color(),
|
||||
)
|
||||
deriv.rect.mob = num
|
||||
deriv.rect.add_updater(lambda r: r.move_to(r.mob))
|
||||
|
||||
self.play(
|
||||
Write(num_words[1]),
|
||||
VGroup(del_derivs, pd_words).shift, DOWN,
|
||||
ShowCreation(del_derivs[1].rect),
|
||||
)
|
||||
self.play(
|
||||
Write(num_words[0]),
|
||||
ShowCreation(del_derivs[0].rect),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class WriteHeatEquation(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
title = TextMobject("The Heat Equation")
|
||||
title.to_edge(UP)
|
||||
|
||||
equation = self.get_d1_equation()
|
||||
equation.next_to(title, DOWN)
|
||||
|
||||
eq_i = equation.index_of_part_by_tex("=")
|
||||
dt_part = equation[:eq_i]
|
||||
dx_part = equation[eq_i + 3:]
|
||||
dt_rect = SurroundingRectangle(dt_part)
|
||||
dt_rect.set_stroke(YELLOW, 2)
|
||||
dx_rect = SurroundingRectangle(dx_part)
|
||||
dx_rect.set_stroke(GREEN, 2)
|
||||
|
||||
two_outlines = equation.get_parts_by_tex("2").copy()
|
||||
two_outlines.set_stroke(YELLOW, 2)
|
||||
two_outlines.set_fill(opacity=0)
|
||||
|
||||
to_be_explained = TextMobject(
|
||||
"To be explained shortly..."
|
||||
)
|
||||
to_be_explained.scale(0.7)
|
||||
to_be_explained.next_to(equation, RIGHT, MED_LARGE_BUFF)
|
||||
to_be_explained.fade(1)
|
||||
|
||||
pde = TextMobject("Partial Differential Equation")
|
||||
pde.move_to(title)
|
||||
|
||||
del_outlines = equation.get_parts_by_tex("\\partial").copy()
|
||||
del_outlines.set_stroke(YELLOW, 2)
|
||||
del_outlines.set_fill(opacity=0)
|
||||
|
||||
self.play(
|
||||
FadeInFrom(title, 0.5 * DOWN),
|
||||
FadeInFrom(equation, 0.5 * UP),
|
||||
)
|
||||
self.wait()
|
||||
self.play(ShowCreation(dt_rect))
|
||||
self.wait()
|
||||
self.play(TransformFromCopy(dt_rect, dx_rect))
|
||||
self.play(ShowCreationThenDestruction(two_outlines))
|
||||
self.wait()
|
||||
self.play(Write(to_be_explained, run_time=1))
|
||||
self.wait(2)
|
||||
self.play(
|
||||
ShowCreationThenDestruction(
|
||||
del_outlines,
|
||||
lag_ratio=0.1,
|
||||
)
|
||||
)
|
||||
self.play(
|
||||
FadeOutAndShift(title, UP),
|
||||
FadeInFrom(pde, DOWN),
|
||||
FadeOut(dt_rect),
|
||||
FadeOut(dx_rect),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class Show3DEquation(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
equation = self.get_d3_equation_with_inputs()
|
||||
equation.set_width(FRAME_WIDTH - 1)
|
||||
inputs = VGroup(*it.chain(*[
|
||||
equation.get_parts_by_tex(s)
|
||||
for s in ["{x}", "{y}", "{z}", "{t}"]
|
||||
]))
|
||||
inputs.sort()
|
||||
equation.to_edge(UP)
|
||||
|
||||
self.add(equation)
|
||||
self.play(LaggedStartMap(
|
||||
ShowCreationThenFadeAround, inputs,
|
||||
surrounding_rectangle_config={
|
||||
"buff": 0.05,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
))
|
||||
self.wait()
|
||||
|
||||
|
||||
class Show1DAnd3DEquations(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
d1_group = self.get_d1_group()
|
||||
d3_group = self.get_d3_group()
|
||||
d1_words, d1_equation = d1_group
|
||||
d3_words, d3_equation = d3_group
|
||||
|
||||
groups = VGroup(d1_group, d3_group)
|
||||
for group in groups:
|
||||
group.arrange(DOWN, buff=MED_LARGE_BUFF)
|
||||
groups.arrange(RIGHT, buff=1.5)
|
||||
groups.to_edge(UP)
|
||||
|
||||
d3_rhs = d3_equation[9:-2]
|
||||
d3_brace = Brace(d3_rhs, DOWN)
|
||||
nabla_words = TextMobject("Sometimes written as")
|
||||
nabla_words.match_width(d3_brace)
|
||||
nabla_words.next_to(d3_brace, DOWN)
|
||||
nabla_exp = TexMobject(
|
||||
"\\nabla^2 {T}",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
nabla_exp.next_to(nabla_words, DOWN)
|
||||
# nabla_group = VGroup(nabla_words, nabla_exp)
|
||||
|
||||
d1_group.save_state()
|
||||
d1_group.center().to_edge(UP)
|
||||
|
||||
self.play(
|
||||
Write(d1_words),
|
||||
FadeInFrom(d1_equation, UP),
|
||||
run_time=1,
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
Restore(d1_group),
|
||||
FadeInFrom(d3_group, LEFT)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
GrowFromCenter(d3_brace),
|
||||
Write(nabla_words),
|
||||
TransformFromCopy(d3_rhs, nabla_exp),
|
||||
run_time=1,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class D1EquationNoInputs(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
equation = self.get_d1_equation_without_inputs()
|
||||
equation.to_edge(UP)
|
||||
# i1 = equation.index_of_part_by_tex("\\partial")
|
||||
# i2 = equation.index_of_part_by_tex("\\cdot")
|
||||
# equation[i1:i1 + 2].set_color(RED)
|
||||
# equation[i2 + 1:i2 + 6].set_color(RED)
|
||||
equation.set_color_by_tex("{T}", RED)
|
||||
self.add(equation)
|
||||
|
||||
|
||||
class AltHeatRHS(Scene):
|
||||
def construct(self):
|
||||
formula = TexMobject(
|
||||
"{\\alpha \\over 2}", "\\Big(",
|
||||
"T({x} - 1, {t}) + T({x} + 1, {t})"
|
||||
"\\Big)",
|
||||
tex_to_color_map={
|
||||
"{x}": GREEN,
|
||||
"{t}": YELLOW,
|
||||
}
|
||||
)
|
||||
self.add(formula)
|
||||
|
||||
|
||||
class CompareInputsOfGeneralCaseTo1D(WriteHeatEquation):
|
||||
def construct(self):
|
||||
three_d_expr, one_d_expr = [
|
||||
TexMobject(
|
||||
"{T}(" + inputs + ", {t})",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
for inputs in ["{x}, {y}, {z}", "{x}"]
|
||||
]
|
||||
for expr in three_d_expr, one_d_expr:
|
||||
expr.scale(2)
|
||||
expr.to_edge(UP)
|
||||
|
||||
x, y, z = [
|
||||
three_d_expr.get_part_by_tex(letter)
|
||||
for letter in ["x", "y", "z"]
|
||||
]
|
||||
|
||||
self.play(FadeInFromDown(three_d_expr))
|
||||
self.play(LaggedStartMap(
|
||||
ShowCreationThenFadeAround,
|
||||
VGroup(x, y, z)
|
||||
))
|
||||
self.wait()
|
||||
low = 3
|
||||
high = -3
|
||||
self.play(
|
||||
ReplacementTransform(three_d_expr[:low], one_d_expr[:low]),
|
||||
ReplacementTransform(three_d_expr[high:], one_d_expr[high:]),
|
||||
three_d_expr[low:high].scale, 0,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ShowLaplacian(WriteHeatEquation):
|
||||
def construct(self):
|
||||
equation = self.get_d3_equation()
|
||||
equation.to_edge(UP, buff=MED_SMALL_BUFF)
|
||||
|
||||
parts = VGroup()
|
||||
plusses = VGroup()
|
||||
for char in "xyz":
|
||||
index = equation.index_of_part_by_tex(
|
||||
"{" + char + "}"
|
||||
)
|
||||
part = equation[index - 6:index + 3]
|
||||
rect = SurroundingRectangle(part)
|
||||
rect.match_color(equation[index])
|
||||
parts.add(part)
|
||||
part.rect = rect
|
||||
if char in "yz":
|
||||
plus = equation[index - 8]
|
||||
part.plus = plus
|
||||
plusses.add(plus)
|
||||
|
||||
lp = equation.get_part_by_tex("(")
|
||||
rp = equation.get_part_by_tex(")")
|
||||
|
||||
for part in parts:
|
||||
part.rp = rp.copy()
|
||||
part.rp.next_to(part, RIGHT, SMALL_BUFF)
|
||||
part.rp.align_to(lp, UP)
|
||||
rp.become(parts[0].rp)
|
||||
|
||||
# Show new second derivatives
|
||||
self.add(*equation)
|
||||
self.remove(*plusses, *parts[1], *parts[2])
|
||||
for part in parts[1:]:
|
||||
self.play(
|
||||
rp.become, part.rp,
|
||||
FadeInFrom(part, LEFT),
|
||||
Write(part.plus),
|
||||
ShowCreation(part.rect),
|
||||
)
|
||||
self.play(
|
||||
FadeOut(part.rect),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# Show laplacian
|
||||
brace = Brace(parts, DOWN)
|
||||
laplacian = TexMobject("\\nabla^2", "T")
|
||||
laplacian.next_to(brace, DOWN)
|
||||
laplacian_name = TextMobject(
|
||||
"``Laplacian''"
|
||||
)
|
||||
laplacian_name.next_to(laplacian, DOWN)
|
||||
|
||||
T_parts = VGroup(*[part[3] for part in parts])
|
||||
non_T_parts = VGroup(*[
|
||||
VGroup(*part[:3], *part[4:])
|
||||
for part in parts
|
||||
])
|
||||
|
||||
self.play(GrowFromCenter(brace))
|
||||
self.play(Write(laplacian_name))
|
||||
self.play(
|
||||
TransformFromCopy(non_T_parts, laplacian[0])
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(T_parts, laplacian[1])
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class AskAboutActuallySolving(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
equation = self.get_d1_equation()
|
||||
equation.center()
|
||||
|
||||
q1 = TextMobject("Solve for T?")
|
||||
q1.next_to(equation, UP, LARGE_BUFF)
|
||||
q2 = TextMobject("What does it \\emph{mean} to solve this?")
|
||||
q2.next_to(equation, UP, LARGE_BUFF)
|
||||
formula = TexMobject(
|
||||
"T({x}, {t}) = \\sin\\big(a{x}\\big) e^{-\\alpha \\cdot a^2 {t}}",
|
||||
tex_to_color_map={
|
||||
"{x}": GREEN,
|
||||
"{t}": YELLOW,
|
||||
}
|
||||
)
|
||||
formula.next_to(equation, DOWN, LARGE_BUFF)
|
||||
q3 = TextMobject("Is this it?")
|
||||
arrow = Vector(LEFT, color=WHITE)
|
||||
arrow.next_to(formula, RIGHT)
|
||||
q3.next_to(arrow, RIGHT)
|
||||
|
||||
self.add(equation)
|
||||
self.play(FadeInFromDown(q1))
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeInFromDown(q2),
|
||||
q1.shift, 1.5 * UP,
|
||||
)
|
||||
self.play(FadeInFrom(formula, UP))
|
||||
self.play(
|
||||
GrowArrow(arrow),
|
||||
FadeInFrom(q3, LEFT)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class PDEPatreonEndscreen(PatreonEndScreen):
|
||||
CONFIG = {
|
||||
"specific_patrons": [
|
||||
"Juan Benet",
|
||||
"Vassili Philippov",
|
||||
"Burt Humburg",
|
||||
"Matt Russell",
|
||||
"Scott Gray",
|
||||
"soekul",
|
||||
"Tihan Seale",
|
||||
"Richard Barthel",
|
||||
"Ali Yahya",
|
||||
"dave nicponski",
|
||||
"Evan Phillips",
|
||||
"Graham",
|
||||
"Joseph Kelly",
|
||||
"Kaustuv DeBiswas",
|
||||
"LambdaLabs",
|
||||
"Lukas Biewald",
|
||||
"Mike Coleman",
|
||||
"Peter Mcinerney",
|
||||
"Quantopian",
|
||||
"Roy Larson",
|
||||
"Scott Walter, Ph.D.",
|
||||
"Yana Chernobilsky",
|
||||
"Yu Jun",
|
||||
"Jordan Scales",
|
||||
"D. Sivakumar",
|
||||
"Lukas -krtek.net- Novy",
|
||||
"John Shaughnessy",
|
||||
"Britt Selvitelle",
|
||||
"David Gow",
|
||||
"J",
|
||||
"Jonathan Wilson",
|
||||
"Joseph John Cox",
|
||||
"Magnus Dahlström",
|
||||
"Randy C. Will",
|
||||
"Ryan Atallah",
|
||||
"Luc Ritchie",
|
||||
"1stViewMaths",
|
||||
"Adrian Robinson",
|
||||
"Alexis Olson",
|
||||
"Andreas Benjamin Brössel",
|
||||
"Andrew Busey",
|
||||
"Ankalagon",
|
||||
"Antoine Bruguier",
|
||||
"Antonio Juarez",
|
||||
"Arjun Chakroborty",
|
||||
"Art Ianuzzi",
|
||||
"Awoo",
|
||||
"Bernd Sing",
|
||||
"Boris Veselinovich",
|
||||
"Brian Staroselsky",
|
||||
"Chad Hurst",
|
||||
"Charles Southerland",
|
||||
"Chris Connett",
|
||||
"Christian Kaiser",
|
||||
"Clark Gaebel",
|
||||
"Cooper Jones",
|
||||
"Danger Dai",
|
||||
"Dave B",
|
||||
"Dave Kester",
|
||||
"David B. Hill",
|
||||
"David Clark",
|
||||
"DeathByShrimp",
|
||||
"Delton Ding",
|
||||
"eaglle",
|
||||
"emptymachine",
|
||||
"Eric Younge",
|
||||
"Eryq Ouithaqueue",
|
||||
"Federico Lebron",
|
||||
"Giovanni Filippi",
|
||||
"Hal Hildebrand",
|
||||
"Hitoshi Yamauchi",
|
||||
"Isaac Jeffrey Lee",
|
||||
"j eduardo perez",
|
||||
"Jacob Magnuson",
|
||||
"Jameel Syed",
|
||||
"Jason Hise",
|
||||
"Jeff Linse",
|
||||
"Jeff Straathof",
|
||||
"John Griffith",
|
||||
"John Haley",
|
||||
"John V Wertheim",
|
||||
"Jonathan Eppele",
|
||||
"Kai-Siang Ang",
|
||||
"Kanan Gill",
|
||||
"L0j1k",
|
||||
"Lee Beck",
|
||||
"Lee Redden",
|
||||
"Linh Tran",
|
||||
"Ludwig Schubert",
|
||||
"Magister Mugit",
|
||||
"Mark B Bahu",
|
||||
"Mark Heising",
|
||||
"Martin Price",
|
||||
"Mathias Jansson",
|
||||
"Matt Langford",
|
||||
"Matt Roveto",
|
||||
"Matthew Bouchard",
|
||||
"Matthew Cocke",
|
||||
"Michael Faust",
|
||||
"Michael Hardel",
|
||||
"Mirik Gogri",
|
||||
"Mustafa Mahdi",
|
||||
"Márton Vaitkus",
|
||||
"Nero Li",
|
||||
"Nikita Lesnikov",
|
||||
"Omar Zrien",
|
||||
"Owen Campbell-Moore",
|
||||
"Peter Ehrnstrom",
|
||||
"RedAgent14",
|
||||
"rehmi post",
|
||||
"Richard Burgmann",
|
||||
"Richard Comish",
|
||||
"Ripta Pasay",
|
||||
"Rish Kundalia",
|
||||
"Robert Teed",
|
||||
"Roobie",
|
||||
"Ryan Williams",
|
||||
"Sachit Nagpal",
|
||||
"Solara570",
|
||||
"Stevie Metke",
|
||||
"Tal Einav",
|
||||
"Ted Suzman",
|
||||
"Thomas Tarler",
|
||||
"Tom Fleming",
|
||||
"Valeriy Skobelev",
|
||||
"Xavier Bernard",
|
||||
"Yavor Ivanov",
|
||||
"Yaw Etse",
|
||||
"YinYangBalance.Asia",
|
||||
"Zach Cardwell",
|
||||
],
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
# Physical constants
|
||||
g = 9.8
|
||||
L = 2
|
||||
mu = 0.1
|
||||
|
||||
THETA_0 = np.pi / 3 # 60 degrees
|
||||
THETA_DOT_0 = 0 # No initial angular velocity
|
||||
|
||||
# Definition of ODE
|
||||
def get_theta_double_dot(theta, theta_dot):
|
||||
return -mu * theta_dot - (g / L) * np.sin(theta)
|
||||
|
||||
|
||||
# Solution to the differential equation
|
||||
def theta(t):
|
||||
# Initialize changing values
|
||||
theta = THETA_0
|
||||
theta_dot = THETA_DOT_0
|
||||
delta_t = 0.01 # Some time step
|
||||
for time in np.arange(0, t, delta_t):
|
||||
# Take many little time steps of size delta_t
|
||||
# until the total time is the function's input
|
||||
theta_double_dot = get_theta_double_dot(
|
||||
theta, theta_dot
|
||||
)
|
||||
theta += theta_dot * delta_t
|
||||
theta_dot += theta_double_dot * delta_t
|
||||
return theta
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
# Helpers
|
||||
def get_shadow(mobject, opacity=0.5):
|
||||
result = mobject.deepcopy()
|
||||
result.apply_function(lambda p: [p[0], p[1], 0])
|
||||
color = interpolate_color(
|
||||
mobject.get_fill_color(), BLACK,
|
||||
mobject.get_fill_opacity()
|
||||
)
|
||||
# color = BLACK
|
||||
result.set_fill(color, opacity=opacity)
|
||||
result.set_stroke(BLACK, 0.5, opacity=opacity)
|
||||
result.set_shade_in_3d(False)
|
||||
return result
|
||||
|
||||
|
||||
def get_boundary_points(shadow, n_points=20):
|
||||
points = shadow.get_points_defining_boundary()
|
||||
return np.array([
|
||||
points[np.argmax(np.dot(points, vect.T))]
|
||||
for vect in compass_directions(n_points)
|
||||
])
|
||||
|
||||
|
||||
def get_area(planar_mobject):
|
||||
boundary = get_boundary_points(planar_mobject, 100)
|
||||
xs = boundary[:, 0]
|
||||
ys = boundary[:, 1]
|
||||
dxs = np.append(xs[-1], xs[:-1]) - xs
|
||||
dys = np.append(ys[-1], ys[:-1]) - ys
|
||||
return abs(sum([
|
||||
0.5 * (x * dy - y * dx)
|
||||
for x, dx, y, dy in zip(xs, dxs, ys, dys)
|
||||
]))
|
||||
|
||||
|
||||
def get_xy_plane_projection_point(p1, p2):
|
||||
"""
|
||||
Draw a line from source to p1 to p2. Where does it
|
||||
intersect the xy plane?
|
||||
"""
|
||||
vect = p2 - p1
|
||||
return p1 - (p1[2] / vect[2]) * vect
|
||||
|
||||
|
||||
# Scenes
|
||||
|
||||
|
||||
class ShowShadows(ThreeDScene):
|
||||
CONFIG = {
|
||||
"object_center": [0, 0, 3],
|
||||
"area_label_center": [0, -1.5, 0],
|
||||
"surface_area": 6.0,
|
||||
"num_reorientations": 10,
|
||||
"camera_config": {
|
||||
"light_source_start_point": 10 * OUT,
|
||||
"frame_center": [0, 0, 0.5],
|
||||
},
|
||||
"initial_orientation_config": {
|
||||
"phi": 60 * DEGREES,
|
||||
"theta": -120 * DEGREES,
|
||||
}
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
self.add_plane()
|
||||
self.setup_orientation_trackers()
|
||||
self.setup_object_and_shadow()
|
||||
self.add_shadow_area_label()
|
||||
self.add_surface_area_label()
|
||||
|
||||
def add_plane(self):
|
||||
plane = self.plane = Rectangle(
|
||||
width=FRAME_WIDTH,
|
||||
height=24.2,
|
||||
stroke_width=0,
|
||||
fill_color=WHITE,
|
||||
fill_opacity=0.35,
|
||||
)
|
||||
plane.set_sheen(0.2, DR)
|
||||
grid = NumberPlane(
|
||||
color=LIGHT_GREY,
|
||||
secondary_color=DARK_GREY,
|
||||
y_radius=int(plane.get_height() / 2),
|
||||
stroke_width=1,
|
||||
secondary_line_ratio=0,
|
||||
)
|
||||
plane.add(grid)
|
||||
plane.add(VectorizedPoint(10 * IN))
|
||||
plane.set_shade_in_3d(True, z_index_as_group=True)
|
||||
self.add(plane)
|
||||
|
||||
def setup_orientation_trackers(self):
|
||||
# Euler angles
|
||||
self.alpha_tracker = ValueTracker(0)
|
||||
self.beta_tracker = ValueTracker(0)
|
||||
self.gamma_tracker = ValueTracker(0)
|
||||
|
||||
def setup_object_and_shadow(self):
|
||||
self.obj3d = always_redraw(self.get_reoriented_object)
|
||||
self.shadow = always_redraw(lambda: get_shadow(self.obj3d))
|
||||
|
||||
def add_shadow_area_label(self):
|
||||
text = TextMobject("Shadow area: ")
|
||||
decimal = DecimalNumber(0)
|
||||
label = VGroup(text, decimal)
|
||||
label.arrange(RIGHT)
|
||||
label.scale(1.5)
|
||||
label.move_to(self.area_label_center - decimal.get_center())
|
||||
self.shadow_area_label = label
|
||||
self.shadow_area_decimal = decimal
|
||||
|
||||
# def update_decimal(decimal):
|
||||
# # decimal.set_value(get_area(self.shadow))
|
||||
# self.add_fixed_in_frame_mobjects(decimal)
|
||||
|
||||
# decimal.add_updater(update_decimal)
|
||||
decimal.add_updater(
|
||||
lambda d: d.set_value(get_area(self.shadow))
|
||||
)
|
||||
decimal.add_updater(
|
||||
lambda d: self.add_fixed_in_frame_mobjects(d)
|
||||
)
|
||||
|
||||
# self.add_fixed_orientation_mobjects(label)
|
||||
self.add_fixed_in_frame_mobjects(label)
|
||||
self.add(label)
|
||||
self.add(decimal)
|
||||
|
||||
def add_surface_area_label(self):
|
||||
text = TextMobject("Surface area: ")
|
||||
decimal = DecimalNumber(self.surface_area)
|
||||
label = VGroup(text, decimal)
|
||||
label.arrange(RIGHT)
|
||||
label.scale(1.25)
|
||||
label.set_fill(YELLOW)
|
||||
label.set_background_stroke(width=3)
|
||||
label.next_to(self.obj3d, RIGHT, LARGE_BUFF)
|
||||
label.shift(MED_LARGE_BUFF * IN)
|
||||
self.surface_area_label = label
|
||||
self.add_fixed_orientation_mobjects(label)
|
||||
|
||||
def construct(self):
|
||||
# Show creation
|
||||
obj3d = self.obj3d.copy()
|
||||
obj3d.clear_updaters()
|
||||
temp_shadow = always_redraw(lambda: get_shadow(obj3d))
|
||||
self.add(temp_shadow)
|
||||
self.move_camera(
|
||||
**self.initial_orientation_config,
|
||||
added_anims=[
|
||||
LaggedStartMap(DrawBorderThenFill, obj3d)
|
||||
],
|
||||
run_time=2
|
||||
)
|
||||
self.begin_ambient_camera_rotation(0.01)
|
||||
self.remove(obj3d, temp_shadow)
|
||||
|
||||
average_label = self.get_average_label()
|
||||
# Reorient
|
||||
self.add(self.obj3d, self.shadow)
|
||||
for n in range(self.num_reorientations):
|
||||
self.randomly_reorient()
|
||||
if n == 3:
|
||||
self.add_fixed_in_frame_mobjects(average_label)
|
||||
self.play(Write(average_label, run_time=2))
|
||||
else:
|
||||
self.wait()
|
||||
|
||||
def randomly_reorient(self, run_time=3):
|
||||
a, b, c = TAU * np.random.random(3)
|
||||
self.play(
|
||||
self.alpha_tracker.set_value, a,
|
||||
self.beta_tracker.set_value, b,
|
||||
self.gamma_tracker.set_value, c,
|
||||
run_time=run_time
|
||||
)
|
||||
|
||||
#
|
||||
def get_object(self):
|
||||
cube = Cube()
|
||||
cube.set_height(1)
|
||||
# cube.set_width(2, stretch=True)
|
||||
cube.set_stroke(WHITE, 0.5)
|
||||
return cube
|
||||
|
||||
def get_reoriented_object(self):
|
||||
obj3d = self.get_object()
|
||||
angles = [
|
||||
self.alpha_tracker.get_value(),
|
||||
self.beta_tracker.get_value(),
|
||||
self.gamma_tracker.get_value(),
|
||||
]
|
||||
vects = [OUT, RIGHT, OUT]
|
||||
|
||||
center = self.object_center
|
||||
obj3d.move_to(center)
|
||||
for angle, vect in zip(angles, vects):
|
||||
obj3d.rotate(angle, vect, about_point=center)
|
||||
return obj3d
|
||||
|
||||
def get_average_label(self):
|
||||
rect = SurroundingRectangle(
|
||||
self.shadow_area_decimal,
|
||||
buff=SMALL_BUFF,
|
||||
color=RED,
|
||||
)
|
||||
words = TextMobject(
|
||||
"Average", "=",
|
||||
"$\\frac{\\text{Surface area}}{4}$"
|
||||
)
|
||||
words.scale(1.5)
|
||||
words[0].match_color(rect)
|
||||
words[2].set_color(self.surface_area_label[0].get_fill_color())
|
||||
words.set_background_stroke(width=3)
|
||||
words.next_to(
|
||||
rect, DOWN,
|
||||
index_of_submobject_to_align=0,
|
||||
)
|
||||
# words.shift(MED_LARGE_BUFF * LEFT)
|
||||
return VGroup(rect, words)
|
||||
|
||||
|
||||
class ShowInfinitelyFarLightSource(ShowShadows):
|
||||
CONFIG = {
|
||||
"num_reorientations": 1,
|
||||
"camera_center": [0, 0, 1],
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.force_skipping()
|
||||
ShowShadows.construct(self)
|
||||
self.revert_to_original_skipping_status()
|
||||
|
||||
self.add_light_source_based_shadow_updater()
|
||||
self.add_light()
|
||||
self.move_light_around()
|
||||
self.show_vertical_lines()
|
||||
|
||||
def add_light(self):
|
||||
light = self.light = self.get_light()
|
||||
light_source = self.camera.light_source
|
||||
light.move_to(light_source)
|
||||
light_source.add_updater(lambda m: m.move_to(light))
|
||||
self.add(light_source)
|
||||
self.add_fixed_orientation_mobjects(light)
|
||||
|
||||
def move_light_around(self):
|
||||
light = self.light
|
||||
self.add_foreground_mobjects(self.shadow_area_label)
|
||||
self.play(
|
||||
light.move_to, 5 * OUT + DOWN,
|
||||
run_time=3
|
||||
)
|
||||
self.play(Rotating(
|
||||
light, angle=TAU, about_point=5 * OUT,
|
||||
rate_func=smooth, run_time=3
|
||||
))
|
||||
self.play(
|
||||
light.move_to, 30 * OUT,
|
||||
run_time=3,
|
||||
)
|
||||
self.remove(light)
|
||||
|
||||
def show_vertical_lines(self):
|
||||
lines = self.get_vertical_lines()
|
||||
obj3d = self.obj3d
|
||||
shadow = self.shadow
|
||||
target_obj3d = obj3d.copy()
|
||||
target_obj3d.become(shadow)
|
||||
target_obj3d.match_style(obj3d)
|
||||
target_obj3d.set_shade_in_3d(False)
|
||||
source_obj3d = obj3d.copy()
|
||||
source_obj3d.set_shade_in_3d(False)
|
||||
source_obj3d.fade(1)
|
||||
|
||||
self.play(LaggedStartMap(ShowCreation, lines))
|
||||
self.wait()
|
||||
self.add(source_obj3d, lines)
|
||||
self.play(
|
||||
ReplacementTransform(source_obj3d, target_obj3d),
|
||||
run_time=2
|
||||
)
|
||||
self.add(target_obj3d, lines)
|
||||
self.play(FadeOut(target_obj3d),)
|
||||
self.wait()
|
||||
lines.add_updater(lambda m: m.become(self.get_vertical_lines()))
|
||||
for x in range(5):
|
||||
self.randomly_reorient()
|
||||
|
||||
def add_light_source_based_shadow_updater(self):
|
||||
shadow = self.shadow
|
||||
light_source = self.camera.light_source
|
||||
obj3d = self.obj3d
|
||||
center = obj3d.get_center()
|
||||
|
||||
def update(shadow):
|
||||
lsp = light_source.get_center()
|
||||
proj_center = get_xy_plane_projection_point(lsp, center)
|
||||
c_to_lsp = lsp - center
|
||||
unit_c_to_lsp = normalize(c_to_lsp)
|
||||
rotation = rotation_matrix(
|
||||
angle=np.arccos(np.dot(unit_c_to_lsp, OUT)),
|
||||
axis=normalize(np.cross(unit_c_to_lsp, OUT))
|
||||
)
|
||||
new_shadow = get_shadow(
|
||||
self.obj3d.copy().apply_matrix(rotation)
|
||||
)
|
||||
shadow.become(new_shadow)
|
||||
shadow.scale(get_norm(lsp) / get_norm(c_to_lsp))
|
||||
shadow.move_to(proj_center)
|
||||
return shadow
|
||||
shadow.add_updater(update)
|
||||
|
||||
def get_light(self):
|
||||
n_rings = 40
|
||||
radii = np.linspace(0, 2, n_rings)
|
||||
rings = VGroup(*[
|
||||
Annulus(inner_radius=r1, outer_radius=r2)
|
||||
for r1, r2 in zip(radii, radii[1:])
|
||||
])
|
||||
opacities = np.linspace(1, 0, n_rings)**1.5
|
||||
for opacity, ring in zip(opacities, rings):
|
||||
ring.set_fill(YELLOW, opacity)
|
||||
ring.set_stroke(YELLOW, width=0.1, opacity=opacity)
|
||||
return rings
|
||||
|
||||
def get_vertical_lines(self):
|
||||
shadow = self.shadow
|
||||
points = get_boundary_points(shadow, 10)
|
||||
# half_points = [(p1 + p2) / 2 for p1, p2 in adjacent_pairs(points)]
|
||||
# points = np.append(points, half_points, axis=0)
|
||||
light_source = self.light.get_center()
|
||||
lines = VGroup(*[
|
||||
DashedLine(light_source, point)
|
||||
for point in points
|
||||
])
|
||||
lines.set_shade_in_3d(True)
|
||||
for line in lines:
|
||||
line.remove(*line[:int(0.8 * len(line))])
|
||||
line[-10:].set_shade_in_3d(False)
|
||||
line.set_stroke(YELLOW, 1)
|
||||
return lines
|
||||
|
||||
|
||||
class CylinderShadows(ShowShadows):
|
||||
CONFIG = {
|
||||
"surface_area": 2 * PI + 2 * PI * 2,
|
||||
"area_label_center": [0, -2, 0],
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
height = 2
|
||||
cylinder = ParametricSurface(
|
||||
lambda u, v: np.array([
|
||||
np.cos(TAU * v),
|
||||
np.sin(TAU * v),
|
||||
height * (1 - u)
|
||||
]),
|
||||
resolution=(6, 32)
|
||||
)
|
||||
# circle = Circle(radius=1)
|
||||
circle = ParametricSurface(
|
||||
lambda u, v: np.array([
|
||||
(v + 0.01) * np.cos(TAU * u),
|
||||
(v + 0.01) * np.sin(TAU * u),
|
||||
0,
|
||||
]),
|
||||
resolution=(16, 8)
|
||||
)
|
||||
# circle.set_fill(GREEN, opacity=0.5)
|
||||
for surface in cylinder, circle:
|
||||
surface.set_fill_by_checkerboard(GREEN, GREEN_E, opacity=1.0)
|
||||
# surface.set_fill(GREEN, opacity=0.5)
|
||||
cylinder.add(circle)
|
||||
cylinder.add(circle.copy().flip().move_to(height * OUT))
|
||||
cylinder.set_shade_in_3d(True)
|
||||
cylinder.set_stroke(width=0)
|
||||
cylinder.scale(1.003)
|
||||
return cylinder
|
||||
|
||||
|
||||
class PrismShadows(ShowShadows):
|
||||
CONFIG = {
|
||||
"surface_area": 3 * np.sqrt(3) / 2 + 3 * (np.sqrt(3) * 2),
|
||||
"object_center": [0, 0, 3],
|
||||
"area_label_center": [0, -2.25, 0],
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
height = 2
|
||||
prism = VGroup()
|
||||
triangle = RegularPolygon(3)
|
||||
verts = triangle.get_anchors()[:3]
|
||||
rects = [
|
||||
Polygon(v1, v2, v2 + height * OUT, v1 + height * OUT)
|
||||
for v1, v2 in adjacent_pairs(verts)
|
||||
]
|
||||
prism.add(triangle, *rects)
|
||||
prism.add(triangle.copy().shift(height * OUT))
|
||||
triangle.reverse_points()
|
||||
prism.set_shade_in_3d(True)
|
||||
prism.set_fill(PINK, 0.8)
|
||||
prism.set_stroke(WHITE, 1)
|
||||
return prism
|
||||
|
||||
|
||||
class TheseFourPiAreSquare(PiCreatureScene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
||||
def create_pi_creatures(self):
|
||||
pass
|
||||
@@ -1,18 +0,0 @@
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
manim:
|
||||
# comment this line if you build the image to prevent overwriting the tag
|
||||
image: eulertour/manim:latest
|
||||
# uncomment this line to build rather than pull the image
|
||||
# build: .
|
||||
volumes:
|
||||
- ${MANIM_PATH:?MANIM_PATH environment variable isn't set}:/opt/manim
|
||||
environment:
|
||||
- PYTHONPATH=/opt/manim
|
||||
working_dir: /opt/manim
|
||||
entrypoint:
|
||||
- python
|
||||
- -m
|
||||
- manim
|
||||
network_mode: "none"
|
||||
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
37
docs/example.py
Normal file
37
docs/example.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from manimlib import *
|
||||
|
||||
class SquareToCircle(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
square = Square()
|
||||
|
||||
self.play(ShowCreation(square))
|
||||
self.wait()
|
||||
self.play(ReplacementTransform(square, circle))
|
||||
self.wait()
|
||||
# Try typing the following lines
|
||||
# self.play(circle.animate.stretch(4, dim=0))
|
||||
# self.play(Rotate(circle, TAU / 4))
|
||||
# self.play(circle.animate.shift(2 * RIGHT), circle.animate.scale(0.25))
|
||||
# circle.insert_n_curves(10)
|
||||
# self.play(circle.animate.apply_complex_function(lambda z: z**2))
|
||||
|
||||
class SquareToCircleEmbed(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
|
||||
self.add(circle)
|
||||
self.wait()
|
||||
self.play(circle.animate.stretch(4, dim=0))
|
||||
self.wait(1.5)
|
||||
self.play(Rotate(circle, TAU / 4))
|
||||
self.wait(1.5)
|
||||
self.play(circle.animate.shift(2 * RIGHT), circle.animate.scale(0.25))
|
||||
self.wait(1.5)
|
||||
circle.insert_n_curves(10)
|
||||
self.play(circle.animate.apply_complex_function(lambda z: z**2))
|
||||
self.wait(2)
|
||||
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
4
docs/requirements.txt
Normal file
4
docs/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Sphinx==3.0.3
|
||||
sphinx-copybutton
|
||||
furo==2020.10.5b9
|
||||
Jinja2
|
||||
BIN
docs/source/_static/icon.png
Normal file
BIN
docs/source/_static/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
43
docs/source/conf.py
Normal file
43
docs/source/conf.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath("."))
|
||||
sys.path.insert(0, os.path.abspath('../../'))
|
||||
|
||||
|
||||
project = 'manim'
|
||||
copyright = '- This document has been placed in the public domain.'
|
||||
author = 'TonyCrane'
|
||||
|
||||
release = ''
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.githubpages',
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx_copybutton',
|
||||
'manim_example_ext'
|
||||
]
|
||||
|
||||
autoclass_content = 'both'
|
||||
mathjax_path = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
|
||||
|
||||
templates_path = ['_templates']
|
||||
source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
pygments_style = 'default'
|
||||
|
||||
html_static_path = ["_static"]
|
||||
html_css_files = [
|
||||
"https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/custom.css",
|
||||
"https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/colors.css"
|
||||
]
|
||||
html_theme = 'furo' # pip install furo==2020.10.5b9
|
||||
html_favicon = '_static/icon.png'
|
||||
html_logo = '../../logo/transparent_graph.png'
|
||||
html_theme_options = {
|
||||
"sidebar_hide_name": True,
|
||||
}
|
||||
31
docs/source/development/about.rst
Normal file
31
docs/source/development/about.rst
Normal file
@@ -0,0 +1,31 @@
|
||||
About
|
||||
=====
|
||||
|
||||
About Manim
|
||||
-----------
|
||||
|
||||
Manim is an animation engine for explanatory math videos.
|
||||
You can use it to make math videos (or other fields) like 3Blue1Brown.
|
||||
|
||||
There are mainly two versions here:
|
||||
|
||||
- `3b1b/manim <https://github.com/3b1b/manim>`_ : Maintained by Grant Sanderson of 3Blue1Brown.
|
||||
|
||||
Using OpenGL and its GLSL language to use GPU for rendering. It has higher efficiency,
|
||||
faster rendering speed, and supports real-time rendering and interaction.
|
||||
|
||||
- `ManimCommunity/manim <https://github.com/ManimCommunity/manim>`_ : Maintained by Manim Community Dev Team.
|
||||
|
||||
Using multiple backend rendering. There is better documentation and
|
||||
a more open contribution community.
|
||||
|
||||
About this documentation
|
||||
------------------------
|
||||
|
||||
This documentation is based on the version in `3b1b/manim <https://github.com/3b1b/manim>`_.
|
||||
Created by `TonyCrane <https://github.com/TonyCrane>`_ ("鹤翔万里" in Chinese) and in production.
|
||||
|
||||
Among them, the ``manim_example_ext`` extension for Sphinx refers to
|
||||
`the documentation of ManimCommunity <https://docs.manim.community/>`_.
|
||||
|
||||
If you want to contribute to manim or this document, please see: :doc:`contributing`
|
||||
211
docs/source/development/changelog.rst
Normal file
211
docs/source/development/changelog.rst
Normal file
@@ -0,0 +1,211 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v1.4.0
|
||||
------
|
||||
|
||||
Fixed bugs
|
||||
^^^^^^^^^^
|
||||
- `f1996f8 <https://github.com/3b1b/manim/pull/1697/commits/f1996f8479f9e33d626b3b66e9eb6995ce231d86>`__: Temporarily fixed ``Lightbulb``
|
||||
- `#1712 <https://github.com/3b1b/manim/pull/1712>`__: Fixed some bugs of ``SVGMobject``
|
||||
- `#1717 <https://github.com/3b1b/manim/pull/1717>`__: Fixed some bugs of SVG path string parser
|
||||
- `#1720 <https://github.com/3b1b/manim/pull/1720>`__: Fixed some bugs of ``MTex``
|
||||
|
||||
New Features
|
||||
^^^^^^^^^^^^
|
||||
- `#1694 <https://github.com/3b1b/manim/pull/1694>`__: Added option to add ticks on x-axis in ``BarChart``
|
||||
- `#1704 <https://github.com/3b1b/manim/pull/1704>`__: Added ``lable_buff`` config parameter for ``Brace``
|
||||
- `#1712 <https://github.com/3b1b/manim/pull/1712>`__: Added support for ``rotate skewX skewY`` transform in SVG
|
||||
- `#1717 <https://github.com/3b1b/manim/pull/1717>`__: Added style support to ``SVGMobject``
|
||||
- `#1719 <https://github.com/3b1b/manim/pull/1719>`__: Added parser to <style> element of SVG
|
||||
- `#1719 <https://github.com/3b1b/manim/pull/1719>`__: Added support for <line> element in ``SVGMobject``
|
||||
|
||||
Refactor
|
||||
^^^^^^^^
|
||||
- `5aa8d15 <https://github.com/3b1b/manim/pull/1697/commits/5aa8d15d85797f68a8f169ca69fd90d441a3abbe>`__: Used ``FFMPEG_BIN`` instead of ``"ffmpeg"`` for sound incorporation
|
||||
- `#1709 <https://github.com/3b1b/manim/pull/1709>`__: Decorated ``CoordinateSystem.get_axes`` and ``.get_all_ranges`` as abstract method
|
||||
- `#1712 <https://github.com/3b1b/manim/pull/1712>`__: Refactored SVG path string parser
|
||||
- `#1712 <https://github.com/3b1b/manim/pull/1712>`__: Allowed ``Mobject.scale`` to receive iterable ``scale_factor``
|
||||
- `#1716 <https://github.com/3b1b/manim/pull/1716>`__: Refactored ``MTex``
|
||||
- `#1721 <https://github.com/3b1b/manim/pull/1721>`__: Improved config helper (``manimgl --config``)
|
||||
- `#1723 <https://github.com/3b1b/manim/pull/1723>`__: Refactored ``MTex``
|
||||
|
||||
Dependencies
|
||||
^^^^^^^^^^^^
|
||||
- `#1719 <https://github.com/3b1b/manim/pull/1719>`__: Added dependency on python package `cssselect2 <https://github.com/Kozea/cssselect2>`__
|
||||
|
||||
|
||||
v1.3.0
|
||||
------
|
||||
|
||||
Fixed bugs
|
||||
^^^^^^^^^^
|
||||
|
||||
- `#1653 <https://github.com/3b1b/manim/pull/1653>`__: Fixed ``Mobject.stretch_to_fit_depth``
|
||||
- `#1655 <https://github.com/3b1b/manim/pull/1655>`__: Fixed the bug of rotating camera
|
||||
- `c73d507 <https://github.com/3b1b/manim/pull/1688/commits/c73d507c76af5c8602d4118bc7538ba04c03ebae>`__: Fixed ``SurfaceMesh`` to be evenly spaced
|
||||
- `82bd02d <https://github.com/3b1b/manim/pull/1688/commits/82bd02d21fbd89b71baa21e077e143f440df9014>`__: Fixed ``angle_between_vectors`` add ``rotation_between_vectors``
|
||||
- `a717314 <https://github.com/3b1b/manim/pull/1688/commits/a7173142bf93fd309def0cc10f3c56f5e6972332>`__: Fixed ``VMobject.fade``
|
||||
- `fbc329d <https://github.com/3b1b/manim/pull/1688/commits/fbc329d7ce3b11821d47adf6052d932f7eff724a>`__: Fixed ``angle_between_vectors``
|
||||
- `bcd0990 <https://github.com/3b1b/manim/pull/1688/commits/bcd09906bea5eaaa5352e7bee8f3153f434cf606>`__: Fixed bug in ``ShowSubmobjectsOneByOne``
|
||||
- `7023548 <https://github.com/3b1b/manim/pull/1691/commits/7023548ec62c4adb2f371aab6a8c7f62deb7c33c>`__: Fixed bug in ``TransformMatchingParts``
|
||||
|
||||
New Features
|
||||
^^^^^^^^^^^^
|
||||
|
||||
- `e10f850 <https://github.com/3b1b/manim/commit/e10f850d0d9f971931cc85d44befe67dc842af6d>`__: Added CLI flag ``--log-level`` to specify log level
|
||||
- `#1667 <https://github.com/3b1b/manim/pull/1667>`__: Added operations (``+`` and ``*``) for ``Mobject``
|
||||
- `#1675 <https://github.com/3b1b/manim/pull/1675>`__: Added 4 boolean operations for ``VMobject`` in ``manimlib/mobject/boolean_ops.py``
|
||||
|
||||
- ``Union(*vmobjects, **kwargs)``
|
||||
- ``Difference(subject, clip, **kwargs)``
|
||||
- ``Intersection(*vmobjects, **kwargs)``
|
||||
- ``Exclusion(*vmobjects, **kwargs)``
|
||||
- `81c3ae3 <https://github.com/3b1b/manim/pull/1688/commits/81c3ae30372e288dc772633dbd17def6e603753e>`__: Added reflectiveness
|
||||
- `2c7689e <https://github.com/3b1b/manim/pull/1688/commits/2c7689ed9e81229ce87c648f97f26267956c0bc9>`__: Enabled ``glow_factor`` on ``DotCloud``
|
||||
- `d065e19 <https://github.com/3b1b/manim/pull/1688/commits/d065e1973d1d6ebd2bece81ce4bdf0c2fff7c772>`__: Added option ``-e`` to insert embed line from the command line
|
||||
- `0e78027 <https://github.com/3b1b/manim/pull/1688/commits/0e78027186a976f7e5fa8d586f586bf6e6baab8d>`__: Improved ``point_from_proportion`` to account for arc length
|
||||
- `781a993 <https://github.com/3b1b/manim/pull/1688/commits/781a9934fda6ba11f22ba32e8ccddcb3ba78592e>`__: Added shortcut ``set_backstroke`` for setting black background stroke
|
||||
- `0b898a5 <https://github.com/3b1b/manim/pull/1688/commits/0b898a5594203668ed9cad38b490ab49ba233bd4>`__: Added ``Suface.always_sort_to_camera``
|
||||
- `e899604 <https://github.com/3b1b/manim/pull/1688/commits/e899604a2d05f78202fcb3b9824ec34647237eae>`__: Added getter methods for specific euler angles
|
||||
- `407c53f <https://github.com/3b1b/manim/pull/1688/commits/407c53f97c061bfd8a53beacd88af4c786f9e9ee>`__: Hade ``rotation_between_vectors`` handle identical/similar vectors
|
||||
- `49743da <https://github.com/3b1b/manim/pull/1688/commits/49743daf3244bfa11a427040bdde8e2bb79589e8>`__: Added ``Mobject.insert_submobject`` method
|
||||
- `9dd1f47 <https://github.com/3b1b/manim/pull/1688/commits/9dd1f47dabca1580d6102e34e44574b0cba556e7>`__: Created single progress display for full scene render
|
||||
- `264f7b1 <https://github.com/3b1b/manim/pull/1691/commits/264f7b11726e9e736f0fe472f66e38539f74e848>`__: Added ``Circle.get_radius``
|
||||
- `83841ae <https://github.com/3b1b/manim/pull/1691/commits/83841ae41568a9c9dff44cd163106c19a74ac281>`__: Added ``Dodecahedron``
|
||||
- `a1d5147 <https://github.com/3b1b/manim/pull/1691/commits/a1d51474ea1ce3b7aa3efbe4c5e221be70ee2f5b>`__: Added ``GlowDot``
|
||||
- `#1678 <https://github.com/3b1b/manim/pull/1678>`__: Added ``MTex`` , see `#1678 <https://github.com/3b1b/manim/pull/1678>`__ for details
|
||||
|
||||
Refactor
|
||||
^^^^^^^^
|
||||
|
||||
- `#1662 <https://github.com/3b1b/manim/pull/1662>`__: Refactored support for command ``A`` in path of SVG
|
||||
- `#1662 <https://github.com/3b1b/manim/pull/1662>`__: Refactored ``SingleStringTex.balance_braces``
|
||||
- `8b454fb <https://github.com/3b1b/manim/pull/1688/commits/8b454fbe9335a7011e947093230b07a74ba9c653>`__: Slight tweaks to how saturation_factor works on newton-fractal
|
||||
- `317a5d6 <https://github.com/3b1b/manim/pull/1688/commits/317a5d6226475b6b54a78db7116c373ef84ea923>`__: Made it possible to set full screen preview as a default
|
||||
- `e764da3 <https://github.com/3b1b/manim/pull/1688/commits/e764da3c3adc5ae2a4ce877b340d2b6abcddc2fc>`__: Used ``quick_point_from_proportion`` for graph points
|
||||
- `d2182b9 <https://github.com/3b1b/manim/pull/1688/commits/d2182b9112300558b6c074cefd685f97c10b3898>`__: Made sure ``Line.set_length`` returns self
|
||||
- `eea3c6b <https://github.com/3b1b/manim/pull/1688/commits/eea3c6b29438f9e9325329c4355e76b9f635e97a>`__: Better align ``SurfaceMesh`` to the corresponding surface polygons
|
||||
- `ee1594a <https://github.com/3b1b/manim/pull/1688/commits/ee1594a3cb7a79b8fc361e4c4397a88c7d20c7e3>`__: Match ``fix_in_frame`` status for ``FlashAround`` mobject
|
||||
- `ba23fbe <https://github.com/3b1b/manim/pull/1688/commits/ba23fbe71e4a038201cd7df1d200514ed1c13bc2>`__: Made sure ``Mobject.is_fixed_in_frame`` stays updated with uniforms
|
||||
- `98b0d26 <https://github.com/3b1b/manim/pull/1691/commits/98b0d266d2475926a606331923cca3dc1dea97ad>`__: Made sure ``skip_animations`` and ``start_at_animation_number`` play well together
|
||||
- `f8e6e7d <https://github.com/3b1b/manim/pull/1691/commits/f8e6e7df3ceb6f3d845ced4b690a85b35e0b8d00>`__: Updated progress display for full scene render
|
||||
- `8f1dfab <https://github.com/3b1b/manim/pull/1691/commits/8f1dfabff04a8456f5c4df75b0f97d50b2755003>`__: ``VectorizedPoint`` should call ``__init__`` for both super classes
|
||||
- `758f329 <https://github.com/3b1b/manim/pull/1691/commits/758f329a06a0c198b27a48c577575d94554305bf>`__: Used array copy when checking need for refreshing triangulation
|
||||
|
||||
|
||||
Dependencies
|
||||
^^^^^^^^^^^^
|
||||
|
||||
- `#1675 <https://github.com/3b1b/manim/pull/1675>`__: Added dependency on python package `skia-pathops <https://github.com/fonttools/skia-pathops>`__
|
||||
|
||||
v1.2.0
|
||||
------
|
||||
|
||||
Fixed bugs
|
||||
^^^^^^^^^^
|
||||
|
||||
- `#1592 <https://github.com/3b1b/manim/pull/1592>`__: Fixed ``put_start_and_end_on`` in 3D
|
||||
- `#1601 <https://github.com/3b1b/manim/pull/1601>`__: Fixed ``DecimalNumber``'s scaling issue
|
||||
- `56df154 <https://github.com/3b1b/manim/commit/56df15453f3e3837ed731581e52a1d76d5692077>`__: Fixed bug with common range array used for all coordinate systems
|
||||
- `8645894 <https://github.com/3b1b/manim/commit/86458942550c639a241267d04d57d0e909fcf252>`__: Fixed ``CoordinateSystem`` init bug
|
||||
- `0dc096b <https://github.com/3b1b/manim/commit/0dc096bf576ea900b351e6f4a80c13a77676f89b>`__: Fixed bug for single-valued ``ValueTracker``
|
||||
- `54ad355 <https://github.com/3b1b/manim/commit/54ad3550ef0c0e2fda46b26700a43fa8cde0973f>`__: Fixed bug with SVG rectangles
|
||||
- `d45ea28 <https://github.com/3b1b/manim/commit/d45ea28dc1d92ab9c639a047c00c151382eb0131>`__: Fixed ``DotCloud.set_radii``
|
||||
- `b543cc0 <https://github.com/3b1b/manim/commit/b543cc0e32d45399ee81638b6d4fb631437664cd>`__: Temporarily fixed bug for ``PMobject`` array resizing
|
||||
- `5f878a2 <https://github.com/3b1b/manim/commit/5f878a2c1aa531b7682bd048468c72d2835c7fe5>`__: Fixed ``match_style``
|
||||
- `719c81d <https://github.com/3b1b/manim/commit/719c81d72b00dcf49f148d7c146774b22e0fe348>`__: Fixed negative ``path_arc`` case
|
||||
- `c726eb7 <https://github.com/3b1b/manim/commit/c726eb7a180b669ee81a18555112de26a8aff6d6>`__: Fixed bug with ``CoordinateSystem.get_lines_parallel_to_axis``
|
||||
- `7732d2f <https://github.com/3b1b/manim/commit/7732d2f0ee10449c5731499396d4911c03e89648>`__: Fixed ``ComplexPlane`` -i display bug
|
||||
|
||||
New Features
|
||||
^^^^^^^^^^^^
|
||||
|
||||
- `#1598 <https://github.com/3b1b/manim/pull/1598>`__: Supported the elliptical arc command ``A`` for ``SVGMobject``
|
||||
- `#1607 <https://github.com/3b1b/manim/pull/1607>`__: Added ``FlashyFadeIn``
|
||||
- `#1607 <https://github.com/3b1b/manim/pull/1607>`__: Save triangulation
|
||||
- `#1625 <https://github.com/3b1b/manim/pull/1625>`__: Added new ``Code`` mobject
|
||||
- `#1637 <https://github.com/3b1b/manim/pull/1637>`__: Add warnings and use rich to display log
|
||||
- `bd356da <https://github.com/3b1b/manim/commit/bd356daa99bfe3134fcb192a5f72e0d76d853801>`__: Added ``VCube``
|
||||
- `6d72893 <https://github.com/3b1b/manim/commit/6d7289338234acc6658b9377c0f0084aa1fa7119>`__: Supported ``ValueTracker`` to track vectors
|
||||
- `3bb8f3f <https://github.com/3b1b/manim/commit/3bb8f3f0422a5dfba0da6ef122dc0c01f31aff03>`__: Added ``set_max_width``, ``set_max_height``, ``set_max_depth`` to ``Mobject``
|
||||
- `a35dd5a <https://github.com/3b1b/manim/commit/a35dd5a3cbdeffa3891d5aa5f80287c18dba2f7f>`__: Added ``TracgTail``
|
||||
- `acba13f <https://github.com/3b1b/manim/commit/acba13f4991b78d54c0bf93cce7ca3b351c25476>`__: Added ``Scene.point_to_mobject``
|
||||
- `f84b8a6 <https://github.com/3b1b/manim/commit/f84b8a66fe9e8b3872e5c716c5c240c14bb555ee>`__: Added poly_fractal shader
|
||||
- `b24ba19 <https://github.com/3b1b/manim/commit/b24ba19dec48ba4e38acbde8eec6d3a308b6ab83>`__: Added kwargs to ``TipableVMobject.set_length``
|
||||
- `17c2772 <https://github.com/3b1b/manim/commit/17c2772b84abf6392a4170030e36e981de4737d0>`__: Added ``Mobject.replicate``
|
||||
- `33fa76d <https://github.com/3b1b/manim/commit/33fa76dfac36e70bb5fad69dc6a336800c6dacce>`__: Added mandelbrot_fractal shader
|
||||
- `f22a341 <https://github.com/3b1b/manim/commit/f22a341e8411eae9331d4dd976b5e15bc6db08d9>`__: Saved state before each embed
|
||||
- `e10a752 <https://github.com/3b1b/manim/commit/e10a752c0001e8981038faa03be4de2603d3565f>`__: Allowed releasing of Textures
|
||||
- `14fbed7 <https://github.com/3b1b/manim/commit/14fbed76da4b493191136caebb8a955e2d41265b>`__: Consolidated and renamed newton_fractal shader
|
||||
- `6cdbe0d <https://github.com/3b1b/manim/commit/6cdbe0d67a11ab14a6d84840a114ae6d3af10168>`__: Hade ``ImageMoject`` remember the filepath to the Image
|
||||
|
||||
Refactor
|
||||
^^^^^^^^
|
||||
|
||||
- `#1601 <https://github.com/3b1b/manim/pull/1601>`__: Changed back to simpler ``Mobject.scale`` implementation
|
||||
- `b667db2 <https://github.com/3b1b/manim/commit/b667db2d311a11cbbca2a6ff511d2c3cf1675486>`__: Simplified ``Square``
|
||||
- `40290ad <https://github.com/3b1b/manim/commit/40290ada8343f10901fa9151cbdf84689667786d>`__: Removed unused parameter ``triangulation_locked``
|
||||
- `8647a64 <https://github.com/3b1b/manim/commit/8647a6429dd0c52cba14e971b8c09194a93cfd87>`__: Reimplemented ``Arrow``
|
||||
- `d8378d8 <https://github.com/3b1b/manim/commit/d8378d8157040cd797cc47ef9576beffd8607863>`__: Used ``make_approximately_smooth`` for ``set_points_smoothly`` by default
|
||||
- `7b4199c <https://github.com/3b1b/manim/commit/7b4199c674e291f1b84678828b63b6bd4fcc6b17>`__: Refactored to call ``_handle_scale_side_effects`` after scaling takes place
|
||||
- `7356a36 <https://github.com/3b1b/manim/commit/7356a36fa70a8279b43ae74e247cbd43b2bfd411>`__: Refactored to only call ``throw_error_if_no_points`` once for ``get_start_and_end``
|
||||
- `0787c4f <https://github.com/3b1b/manim/commit/0787c4f36270a6560b50ce3e07b30b0ec5f2ba3e>`__: Made sure framerate is 30 for previewed scenes
|
||||
- `c635f19 <https://github.com/3b1b/manim/commit/c635f19f2a33e916509e53ded46f55e2afa8f5f2>`__: Pushed ``pixel_coords_to_space_coords`` to ``Window``
|
||||
- `d5a88d0 <https://github.com/3b1b/manim/commit/d5a88d0fa457cfcf4cb9db417a098c37c95c7051>`__: Refactored to pass tuples and not arrays to uniforms
|
||||
- `9483f26 <https://github.com/3b1b/manim/commit/9483f26a3b056de0e34f27acabd1a946f1adbdf9>`__: Refactored to copy uniform arrays in ``Mobject.copy``
|
||||
- `ed1fc4d <https://github.com/3b1b/manim/commit/ed1fc4d5f94467d602a568466281ca2d0368b506>`__: Added ``bounding_box`` as exceptional key to point_cloud mobject
|
||||
- `329d2c6 <https://github.com/3b1b/manim/commit/329d2c6eaec3d88bfb754b555575a3ea7c97a7e0>`__: Made sure stroke width is always a float
|
||||
|
||||
|
||||
v1.1.0
|
||||
-------
|
||||
|
||||
Fixed bugs
|
||||
^^^^^^^^^^
|
||||
|
||||
- Fixed the bug of :func:`~manimlib.utils.iterables.resize_with_interpolation` in the case of ``length=0``
|
||||
- Fixed the bug of ``__init__`` in :class:`~manimlib.mobject.geometry.Elbow`
|
||||
- If chosen monitor is not available, choose one that does exist
|
||||
- Make sure mobject data gets unlocked after animations
|
||||
- Fixed a bug for off-center vector fields
|
||||
- Had ``Mobject.match_points`` return self
|
||||
- Fixed chaining animation in example scenes
|
||||
- Fixed the default color of tip
|
||||
- Fixed a typo in ``ShowPassingFlashWithThinningStrokeWidth``
|
||||
- Fixed the default size of ``Text``
|
||||
- Fixed a missing import line in ``mobject.py``
|
||||
- Fixed the bug in ControlsExample
|
||||
- Make sure frame is added to the scene when initialization
|
||||
- Fixed zooming directions
|
||||
- Rewrote ``earclip_triangulation`` to fix triangulation
|
||||
- Allowed sound_file_name to be taken in without extensions
|
||||
|
||||
New Features
|
||||
^^^^^^^^^^^^
|
||||
|
||||
- Added :class:`~manimlib.animation.indication.VShowPassingFlash`
|
||||
- Added ``COLORMAP_3B1B``
|
||||
- Added some methods to coordinate system to access all axes ranges
|
||||
|
||||
- :meth:`~manimlib.mobject.coordinate_systems.CoordinateSystem.get_origin`
|
||||
- :meth:`~manimlib.mobject.coordinate_systems.CoordinateSystem.get_all_ranges`
|
||||
- Added :meth:`~manimlib.mobject.mobject.Mobject.set_color_by_rgba_func`
|
||||
- Updated :class:`~manimlib.mobject.vector_field.VectorField` and :class:`~manimlib.mobject.vector_field.StreamLines`
|
||||
- Allow ``3b1b_colormap`` as an option for :func:`~manimlib.utils.color.get_colormap_list`
|
||||
- Return ``stroke_width`` as 1d array
|
||||
- Added :meth:`~manimlib.mobject.svg.text_mobject.Text.get_parts_by_text`
|
||||
- Use Text not TexText for Brace
|
||||
- Update to Cross to make it default to variable stroke width
|
||||
- Added :class:`~manimlib.animation.indication.FlashAround` and :class:`~manimlib.animation.indication.FlashUnder`
|
||||
- Allowed configuration in ``Brace.get_text``
|
||||
- Added :meth:`~manimlib.camera.camera.CameraFrame.reorient` for quicker changes to frame angle
|
||||
- Added ``units`` to :meth:`~manimlib.camera.camera.CameraFrame.set_euler_angles`
|
||||
- Allowed any ``VMobject`` to be passed into ``TransformMatchingTex``
|
||||
- Removed double brace convention in ``Tex`` and ``TexText``
|
||||
- Added support for debugger launch
|
||||
- Added CLI flag ``--config_file`` to load configuration file manually
|
||||
- Added ``tip_style`` to ``tip_config``
|
||||
- Added ``MarkupText``
|
||||
- Take in ``u_range`` and ``v_range`` as arguments to ``ParametricSurface``
|
||||
- Added ``TrueDot``
|
||||
59
docs/source/development/contributing.rst
Normal file
59
docs/source/development/contributing.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
Contributing
|
||||
============
|
||||
|
||||
Accept any contribution you make :)
|
||||
|
||||
- **Contribute to the manim source code**:
|
||||
|
||||
Please fork to your own repository and make changes, submit a pull request, and fill in
|
||||
the motivation for the change following the instructions in the template. We will check
|
||||
your pull request in detail (this usually takes a while, please be patient)
|
||||
|
||||
- **Contribute to the documentation**:
|
||||
|
||||
Also submit a pull request and write down the main changes.
|
||||
|
||||
- **If you find a bug in the code**:
|
||||
|
||||
Please open an issue and fill in the found problem and your environment according
|
||||
to the template. (But please note that if you think this problem is just a problem
|
||||
of yourself, rather than a problem of source code, it is recommended that you ask a
|
||||
question in the `Q&A category <https://github.com/3b1b/manim/discussions/categories/q-a>`_
|
||||
of the discussion page)
|
||||
|
||||
- **You are welcome to share the content you made with manim**:
|
||||
|
||||
Post it in the `show and tell category <https://github.com/3b1b/manim/discussions/categories/show-and-tell>`_
|
||||
of the discussion page.
|
||||
|
||||
- **You are also welcome to share some of your suggestions and ideas**:
|
||||
|
||||
Post them in the `ideas category <https://github.com/3b1b/manim/discussions/categories/ideas>`_
|
||||
of the discussion page.
|
||||
|
||||
How to build this documentation
|
||||
-------------------------------
|
||||
|
||||
- Clone the 3b1b/manim repository
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
git clone https://github.com/3b1b/manim.git
|
||||
# or your own repo
|
||||
# git clone https://github.com/<your user name>/manim.git
|
||||
cd manim
|
||||
|
||||
- Install python package dependencies
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install -r docs/requirements.txt
|
||||
|
||||
- Go to the ``docs/`` folder and build
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
cd docs/
|
||||
make html
|
||||
|
||||
- The output document is located in ``docs/build/html/``
|
||||
2
docs/source/documentation/animation/index.rst
Normal file
2
docs/source/documentation/animation/index.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Animation (TODO)
|
||||
================
|
||||
2
docs/source/documentation/camera/index.rst
Normal file
2
docs/source/documentation/camera/index.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Camera (TODO)
|
||||
=============
|
||||
192
docs/source/documentation/constants.rst
Normal file
192
docs/source/documentation/constants.rst
Normal file
@@ -0,0 +1,192 @@
|
||||
constants
|
||||
=========
|
||||
|
||||
The ``constants.py`` in the ``manimlib`` folder defines the constants
|
||||
needed when running manim. Some constants are not explained here because
|
||||
they are only used inside manim.
|
||||
|
||||
Frame and pixel shape
|
||||
---------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ASPECT_RATIO = 16.0 / 9.0
|
||||
FRAME_HEIGHT = 8.0
|
||||
FRAME_WIDTH = FRAME_HEIGHT * ASPECT_RATIO
|
||||
FRAME_Y_RADIUS = FRAME_HEIGHT / 2
|
||||
FRAME_X_RADIUS = FRAME_WIDTH / 2
|
||||
|
||||
DEFAULT_PIXEL_HEIGHT = 1080
|
||||
DEFAULT_PIXEL_WIDTH = 1920
|
||||
DEFAULT_FRAME_RATE = 30
|
||||
|
||||
Buffs
|
||||
-----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SMALL_BUFF = 0.1
|
||||
MED_SMALL_BUFF = 0.25
|
||||
MED_LARGE_BUFF = 0.5
|
||||
LARGE_BUFF = 1
|
||||
|
||||
DEFAULT_MOBJECT_TO_EDGE_BUFFER = MED_LARGE_BUFF # Distance between object and edge
|
||||
DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = MED_SMALL_BUFF # Distance between objects
|
||||
|
||||
Run times
|
||||
---------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0
|
||||
DEFAULT_WAIT_TIME = 1.0
|
||||
|
||||
Coordinates
|
||||
-----------
|
||||
|
||||
manim uses three-dimensional coordinates and uses the type of ``ndarray``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ORIGIN = np.array((0., 0., 0.))
|
||||
UP = np.array((0., 1., 0.))
|
||||
DOWN = np.array((0., -1., 0.))
|
||||
RIGHT = np.array((1., 0., 0.))
|
||||
LEFT = np.array((-1., 0., 0.))
|
||||
IN = np.array((0., 0., -1.))
|
||||
OUT = np.array((0., 0., 1.))
|
||||
X_AXIS = np.array((1., 0., 0.))
|
||||
Y_AXIS = np.array((0., 1., 0.))
|
||||
Z_AXIS = np.array((0., 0., 1.))
|
||||
|
||||
# Useful abbreviations for diagonals
|
||||
UL = UP + LEFT
|
||||
UR = UP + RIGHT
|
||||
DL = DOWN + LEFT
|
||||
DR = DOWN + RIGHT
|
||||
|
||||
TOP = FRAME_Y_RADIUS * UP
|
||||
BOTTOM = FRAME_Y_RADIUS * DOWN
|
||||
LEFT_SIDE = FRAME_X_RADIUS * LEFT
|
||||
RIGHT_SIDE = FRAME_X_RADIUS * RIGHT
|
||||
|
||||
Mathematical constant
|
||||
---------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
PI = np.pi
|
||||
TAU = 2 * PI
|
||||
DEGREES = TAU / 360
|
||||
|
||||
Text
|
||||
----
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
START_X = 30
|
||||
START_Y = 20
|
||||
NORMAL = "NORMAL"
|
||||
ITALIC = "ITALIC"
|
||||
OBLIQUE = "OBLIQUE"
|
||||
BOLD = "BOLD"
|
||||
|
||||
Stroke width
|
||||
------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
DEFAULT_STROKE_WIDTH = 4
|
||||
|
||||
Colours
|
||||
-------
|
||||
|
||||
Here are the preview of default colours. (Modified from
|
||||
`elteoremadebeethoven <https://elteoremadebeethoven.github.io/manim_3feb_docs.github.io/html/_static/colors/colors.html>`_)
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div style="float: left;">
|
||||
<h3>BLUE</h3>
|
||||
<div class="colors BLUE_E"><p class="color-text">BLUE_E</p></div>
|
||||
<div class="colors BLUE_D"><p class="color-text">BLUE_D</p></div>
|
||||
<div class="colors BLUE_C"><p class="color-text">BLUE_C</p></div>
|
||||
<div class="colors BLUE_B"><p class="color-text">BLUE_B</p></div>
|
||||
<div class="colors BLUE_A"><p class="color-text">BLUE_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>TEAL</h3>
|
||||
<div class="colors TEAL_E"><p class="color-text">TEAL_E</p></div>
|
||||
<div class="colors TEAL_D"><p class="color-text">TEAL_D</p></div>
|
||||
<div class="colors TEAL_C"><p class="color-text">TEAL_C</p></div>
|
||||
<div class="colors TEAL_B"><p class="color-text">TEAL_B</p></div>
|
||||
<div class="colors TEAL_A"><p class="color-text">TEAL_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>GREEN</h3>
|
||||
<div class="colors GREEN_E"><p class="color-text">GREEN_E</p></div>
|
||||
<div class="colors GREEN_D"><p class="color-text">GREEN_D</p></div>
|
||||
<div class="colors GREEN_C"><p class="color-text">GREEN_C</p></div>
|
||||
<div class="colors GREEN_B"><p class="color-text">GREEN_B</p></div>
|
||||
<div class="colors GREEN_A"><p class="color-text">GREEN_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>YELLOW</h3>
|
||||
<div class="colors YELLOW_E"><p class="color-text">YELLOW_E</p></div>
|
||||
<div class="colors YELLOW_D"><p class="color-text">YELLOW_D</p></div>
|
||||
<div class="colors YELLOW_C"><p class="color-text">YELLOW_C</p></div>
|
||||
<div class="colors YELLOW_B"><p class="color-text">YELLOW_B</p></div>
|
||||
<div class="colors YELLOW_A"><p class="color-text">YELLOW_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>GOLD</h3>
|
||||
<div class="colors GOLD_E"><p class="color-text">GOLD_E</p></div>
|
||||
<div class="colors GOLD_D"><p class="color-text">GOLD_D</p></div>
|
||||
<div class="colors GOLD_C"><p class="color-text">GOLD_C</p></div>
|
||||
<div class="colors GOLD_B"><p class="color-text">GOLD_B</p></div>
|
||||
<div class="colors GOLD_A"><p class="color-text">GOLD_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>RED</h3>
|
||||
<div class="colors RED_E"><p class="color-text">RED_E</p></div>
|
||||
<div class="colors RED_D"><p class="color-text">RED_D</p></div>
|
||||
<div class="colors RED_C"><p class="color-text">RED_C</p></div>
|
||||
<div class="colors RED_B"><p class="color-text">RED_B</p></div>
|
||||
<div class="colors RED_A"><p class="color-text">RED_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>MAROON</h3>
|
||||
<div class="colors MAROON_E"><p class="color-text">MAROON_E</p></div>
|
||||
<div class="colors MAROON_D"><p class="color-text">MAROON_D</p></div>
|
||||
<div class="colors MAROON_C"><p class="color-text">MAROON_C</p></div>
|
||||
<div class="colors MAROON_B"><p class="color-text">MAROON_B</p></div>
|
||||
<div class="colors MAROON_A"><p class="color-text">MAROON_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>PURPLE</h3>
|
||||
<div class="colors PURPLE_E"><p class="color-text">PURPLE_E</p></div>
|
||||
<div class="colors PURPLE_D"><p class="color-text">PURPLE_D</p></div>
|
||||
<div class="colors PURPLE_C"><p class="color-text">PURPLE_C</p></div>
|
||||
<div class="colors PURPLE_B"><p class="color-text">PURPLE_B</p></div>
|
||||
<div class="colors PURPLE_A"><p class="color-text">PURPLE_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>GREY</h3>
|
||||
<div class="colors GREY_E"><p class="color-text">GREY_E</p></div>
|
||||
<div class="colors GREY_D"><p class="color-text">GREY_D</p></div>
|
||||
<div class="colors GREY_C"><p class="color-text">GREY_C</p></div>
|
||||
<div class="colors GREY_B"><p class="color-text">GREY_B</p></div>
|
||||
<div class="colors GREY_A"><p class="color-text">GREY_A</p></div>
|
||||
</div>
|
||||
<div style="float: left;">
|
||||
<h3>Others</h3>
|
||||
<div class="colors WHITE"><p class="color-text" style="color: BLACK">WHITE</p></div>
|
||||
<div class="colors BLACK"><p class="color-text">BLACK</p></div>
|
||||
<div class="colors GREY_BROWN"><p class="color-text-small">GREY_BROWN</p></div>
|
||||
<div class="colors DARK_BROWN"><p class="color-text-small">DARK_BROWN</p></div>
|
||||
<div class="colors LIGHT_BROWN"><p class="color-text-small">LIGHT_BROWN</p></div>
|
||||
<div class="colors PINK"><p class="color-text">PINK</p></div>
|
||||
<div class="colors LIGHT_PINK"><p class="color-text-small">LIGHT_PINK</p></div>
|
||||
<div class="colors GREEN_SCREEN"><p class="color-text-small">GREEN_SCREEN</p></div>
|
||||
<div class="colors ORANGE"><p class="color-text">ORANGE</p></div>
|
||||
</div>
|
||||
144
docs/source/documentation/custom_config.rst
Normal file
144
docs/source/documentation/custom_config.rst
Normal file
@@ -0,0 +1,144 @@
|
||||
custom_config
|
||||
==============
|
||||
|
||||
``directories``
|
||||
---------------
|
||||
|
||||
- ``mirror_module_path``
|
||||
(``True`` or ``False``) Whether to create a folder named the name of the
|
||||
running file under the ``output`` path, and save the output (``images/``
|
||||
or ``videos/``) in it.
|
||||
|
||||
- ``output``
|
||||
Output file path, the videos will be saved in the ``videos/`` folder under it,
|
||||
and the pictures will be saved in the ``images/`` folder under it.
|
||||
|
||||
For example, if you set ``output`` to ``"/.../manim/output"`` and
|
||||
``mirror_module_path`` to ``False``, then you exported ``Scene1`` in the code
|
||||
file and saved the last frame, then the final directory structure will be like:
|
||||
|
||||
.. code-block:: text
|
||||
:emphasize-lines: 9, 11
|
||||
|
||||
manim/
|
||||
├── manimlib/
|
||||
│ ├── animation/
|
||||
│ ├── ...
|
||||
│ ├── default_config.yml
|
||||
│ └── window.py
|
||||
├── output/
|
||||
│ ├── images
|
||||
│ │ └── Scene1.png
|
||||
│ └── videos
|
||||
│ └── Scene1.mp4
|
||||
├── code.py
|
||||
└── custom_config.yml
|
||||
|
||||
But if you set ``mirror_module_path`` to ``True``, the directory structure will be:
|
||||
|
||||
.. code-block:: text
|
||||
:emphasize-lines: 8
|
||||
|
||||
manim/
|
||||
├── manimlib/
|
||||
│ ├── animation/
|
||||
│ ├── ...
|
||||
│ ├── default_config.yml
|
||||
│ └── window.py
|
||||
├── output/
|
||||
│ └── code/
|
||||
│ ├── images
|
||||
│ │ └── Scene1.png
|
||||
│ └── videos
|
||||
│ └── Scene1.mp4
|
||||
├── code.py
|
||||
└── custom_config.yml
|
||||
|
||||
- ``raster_images``
|
||||
The directory for storing raster images to be used in the code (including
|
||||
``.jpg``, ``.jpeg``, ``.png`` and ``.gif``), which will be read by ``ImageMobject``.
|
||||
|
||||
- ``vector_images``
|
||||
The directory for storing vector images to be used in the code (including
|
||||
``.svg`` and ``.xdv``), which will be read by ``SVGMobject``.
|
||||
|
||||
- ``sounds``
|
||||
The directory for storing sound files to be used in ``Scene.add_sound()`` (
|
||||
including ``.wav`` and ``.mp3``).
|
||||
|
||||
- ``temporary_storage``
|
||||
The directory for storing temporarily generated cache files, including
|
||||
``Tex`` cache, ``Text`` cache and storage of object points.
|
||||
|
||||
``tex``
|
||||
-------
|
||||
|
||||
- ``executable``
|
||||
The executable program used to compile LaTeX (``latex`` or ``xelatex -no-pdf``
|
||||
is recommended)
|
||||
|
||||
- ``template_file``
|
||||
LaTeX template used, in ``manimlib/tex_templates``
|
||||
|
||||
- ``intermediate_filetype``
|
||||
The type of intermediate vector file generated after compilation (``dvi`` if
|
||||
``latex`` is used, ``xdv`` if ``xelatex`` is used)
|
||||
|
||||
- ``text_to_replace``
|
||||
The text to be replaced in the template (needn't to change)
|
||||
|
||||
``universal_import_line``
|
||||
-------------------------
|
||||
|
||||
Import line that need to execute when entering interactive mode directly.
|
||||
|
||||
``style``
|
||||
---------
|
||||
|
||||
- ``font``
|
||||
Default font of Text
|
||||
|
||||
- ``background_color``
|
||||
Default background color
|
||||
|
||||
``window_position``
|
||||
-------------------
|
||||
|
||||
The relative position of the playback window on the display (two characters,
|
||||
the first character means upper(U) / middle(O) / lower(D), the second character
|
||||
means left(L) / middle(O) / right(R)).
|
||||
|
||||
``window_monitor``
|
||||
------------------
|
||||
|
||||
The number of the monitor you want the preview window to pop up on. (default is 0)
|
||||
|
||||
``break_into_partial_movies``
|
||||
-----------------------------
|
||||
|
||||
If this is set to ``True``, then many small files will be written corresponding
|
||||
to each ``Scene.play`` and ``Scene.wait`` call, and these files will then be combined
|
||||
to form the full scene.
|
||||
|
||||
Sometimes video-editing is made easier when working with the broken up scene, which
|
||||
effectively has cuts at all the places you might want.
|
||||
|
||||
``camera_qualities``
|
||||
--------------------
|
||||
|
||||
Export quality
|
||||
|
||||
- ``low``
|
||||
Low quality (default is 480p15)
|
||||
|
||||
- ``medium``
|
||||
Medium quality (default is 720p30)
|
||||
|
||||
- ``high``
|
||||
High quality (default is 1080p30)
|
||||
|
||||
- ``ultra_high``
|
||||
Ultra high quality (default is 4K60)
|
||||
|
||||
- ``default_quality``
|
||||
Default quality (one of the above four)
|
||||
2
docs/source/documentation/mobject/index.rst
Normal file
2
docs/source/documentation/mobject/index.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Mobject (TODO)
|
||||
==============
|
||||
2
docs/source/documentation/scene/index.rst
Normal file
2
docs/source/documentation/scene/index.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Scene (TODO)
|
||||
============
|
||||
2
docs/source/documentation/shaders/index.rst
Normal file
2
docs/source/documentation/shaders/index.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Shaders (TODO)
|
||||
==============
|
||||
2
docs/source/documentation/utils/index.rst
Normal file
2
docs/source/documentation/utils/index.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
Utils (TODO)
|
||||
============
|
||||
85
docs/source/getting_started/config.rst
Normal file
85
docs/source/getting_started/config.rst
Normal file
@@ -0,0 +1,85 @@
|
||||
CONFIG dictionary
|
||||
=================
|
||||
|
||||
What's CONFIG
|
||||
-------------
|
||||
|
||||
``CONFIG`` dictionary is a feature of manim, which facilitates the inheritance
|
||||
and modification of parameters between parent and child classes.
|
||||
|
||||
| ``CONFIG`` dictionary 's processing is in ``manimlib/utils/config_ops.py``
|
||||
| It can convert the key-value pairs in the ``CONFIG`` dictionary into class attributes and values
|
||||
|
||||
Generally, the first line of the ``.__init__()`` method in some basic class (``Mobject``, ``Animation``,
|
||||
etc.) will call this function ``digest_config(self, kwargs)`` to convert both
|
||||
the ``CONFIG`` dictionary and ``kwargs`` into attributes. Then it can be accessed
|
||||
directly through ``self.``, which simplifies the handling of inheritance between classes.
|
||||
|
||||
**An example**:
|
||||
|
||||
There are many class inheritance relationships in ``manimlib/mobject/geometry.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Line 279
|
||||
class Circle(Arc):
|
||||
CONFIG = {
|
||||
"color": RED,
|
||||
"close_new_points": True,
|
||||
"anchors_span_full_range": False
|
||||
}
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Line 304
|
||||
class Dot(Circle):
|
||||
CONFIG = {
|
||||
"radius": DEFAULT_DOT_RADIUS,
|
||||
"stroke_width": 0,
|
||||
"fill_opacity": 1.0,
|
||||
"color": WHITE
|
||||
}
|
||||
|
||||
The ``Circle`` class uses the key-value pair ``"color": RED`` in the ``CONFIG``
|
||||
dictionary to add the attribute ``self.color``.
|
||||
|
||||
At the same time, the ``Dot`` class also contains the key ``color`` in the
|
||||
``CONFIG`` dictionary, but the value is different. At this time, the priority will
|
||||
modify the attribute ``self.color`` to ``WHITE``.
|
||||
|
||||
CONFIG nesting
|
||||
--------------
|
||||
|
||||
The ``CONFIG`` dictionary supports nesting, that is, the value of the key is also
|
||||
a dictionary, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Camera(object):
|
||||
CONFIG = {
|
||||
# configs
|
||||
}
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Scene(object):
|
||||
CONFIG = {
|
||||
"window_config": {},
|
||||
"camera_class": Camera,
|
||||
"camera_config": {},
|
||||
"file_writer_config": {},
|
||||
# other configs
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
# some lines
|
||||
self.camera = self.camera_class(**self.camera_config)
|
||||
|
||||
The ``CONFIG`` dictionary of the ``Camera`` class contains many key-value pairs,
|
||||
and this class needs to be instantiated in the ``Scene`` class. For more convenient
|
||||
control, there is a special key-value pair in the Scene class ``"camera_config": {}``,
|
||||
Its value is a dictionary, passed in as ``kwargs`` when initializing the ``Camera`` class
|
||||
to modify the value of the properties of the ``Camera`` class.
|
||||
|
||||
So the nesting of the ``CONFIG`` dictionary **essentially** passes in the value as ``kwargs``.
|
||||
97
docs/source/getting_started/configuration.rst
Normal file
97
docs/source/getting_started/configuration.rst
Normal file
@@ -0,0 +1,97 @@
|
||||
CLI flags and configuration
|
||||
===========================
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
To run manim, you need to enter the directory at the same level as ``manimlib/``
|
||||
and enter the command in the following format into terminal:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
manimgl <code>.py <Scene> <flags>
|
||||
# or
|
||||
manim-render <code>.py <Scene> <flags>
|
||||
|
||||
- ``<code>.py`` : The python file you wrote. Needs to be at the same level as ``manimlib/``, otherwise you need to use an absolute path or a relative path.
|
||||
- ``<Scene>`` : The scene you want to render here. If it is not written or written incorrectly, it will list all for you to choose. And if there is only one ``Scene`` in the file, this class will be rendered directly.
|
||||
- ``<flags>`` : CLI flags.
|
||||
|
||||
Some useful flags
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
- ``-w`` to write the scene to a file.
|
||||
- ``-o`` to write the scene to a file and open the result.
|
||||
- ``-s`` to skip to the end and just show the final frame.
|
||||
|
||||
- ``-so`` will save the final frame to an image and show it.
|
||||
|
||||
- ``-n <number>`` to skip ahead to the ``n``\ ’th animation of a scene.
|
||||
- ``-f`` to make the playback window fullscreen.
|
||||
|
||||
All supported flags
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
========================================================== ====== =================================================================================================================================================================================================
|
||||
flag abbr function
|
||||
========================================================== ====== =================================================================================================================================================================================================
|
||||
``--help`` ``-h`` Show the help message and exit
|
||||
``--write_file`` ``-w`` Render the scene as a movie file
|
||||
``--skip_animations`` ``-s`` Skip to the last frame
|
||||
``--low_quality`` ``-l`` Render at a low quality (for faster rendering)
|
||||
``--medium_quality`` ``-m`` Render at a medium quality
|
||||
``--hd`` Render at a 1080p quality
|
||||
``--uhd`` Render at a 4k quality
|
||||
``--full_screen`` ``-f`` Show window in full screen
|
||||
``--save_pngs`` ``-g`` Save each frame as a png
|
||||
``--save_as_gif`` ``-i`` Save the video as gif
|
||||
``--transparent`` ``-t`` Render to a movie file with an alpha channel
|
||||
``--quiet`` ``-q``
|
||||
``--write_all`` ``-a`` Write all the scenes from a file
|
||||
``--open`` ``-o`` Automatically open the saved file once its done
|
||||
``--finder`` Show the output file in finder
|
||||
``--config`` Guide for automatic configuration
|
||||
``--file_name FILE_NAME`` Name for the movie or image file
|
||||
``--start_at_animation_number START_AT_ANIMATION_NUMBER`` ``-n`` Start rendering not from the first animation, but from another, specified by its index. If you passing two comma separated values, e.g. "3,6", it will end the rendering at the second value.
|
||||
``--embed LINENO`` ``-e`` Takes a line number as an argument, and results in the scene being called as if the line ``self.embed()`` was inserted into the scene code at that line number
|
||||
``--resolution RESOLUTION`` ``-r`` Resolution, passed as "WxH", e.g. "1920x1080"
|
||||
``--frame_rate FRAME_RATE`` Frame rate, as an integer
|
||||
``--color COLOR`` ``-c`` Background color
|
||||
``--leave_progress_bars`` Leave progress bars displayed in terminal
|
||||
``--video_dir VIDEO_DIR`` directory to write video
|
||||
``--config_file CONFIG_FILE`` Path to the custom configuration file
|
||||
========================================================== ====== =================================================================================================================================================================================================
|
||||
|
||||
custom_config
|
||||
--------------
|
||||
|
||||
In order to perform more configuration (about directories, etc.) and permanently
|
||||
change the default value (you don't have to add flags to the command every time),
|
||||
you can modify ``custom_config.yml``. The meaning of each option is in
|
||||
page :doc:`../documentation/custom_config`.
|
||||
|
||||
You can also use different ``custom_config.yml`` for different directories, such as
|
||||
following the directory structure:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
manim/
|
||||
├── manimlib/
|
||||
│ ├── animation/
|
||||
│ ├── ...
|
||||
│ ├── default_config.yml
|
||||
│ └── window.py
|
||||
├── project/
|
||||
│ ├── code.py
|
||||
│ └── custom_config.yml
|
||||
└── custom_config.yml
|
||||
|
||||
When you enter the ``project/`` folder and run ``manimgl code.py <Scene>``,
|
||||
it will overwrite ``manim/default_config.yml`` with ``custom_config.yml``
|
||||
in the ``project`` folder.
|
||||
|
||||
Alternatively, you can use ``--config_file`` flag in CLI to specify configuration file manually.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
manimgl project/code.py --config_file /path/to/custom_config.yml
|
||||
726
docs/source/getting_started/example_scenes.rst
Normal file
726
docs/source/getting_started/example_scenes.rst
Normal file
@@ -0,0 +1,726 @@
|
||||
Example Scenes
|
||||
==============
|
||||
|
||||
After understanding the previous knowledge, we can understand more scenes.
|
||||
Many example scenes are given in ``example_scenes.py``, let's start with
|
||||
the simplest and one by one.
|
||||
|
||||
InteractiveDevlopment
|
||||
---------------------
|
||||
|
||||
.. manim-example:: InteractiveDevelopment
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/InteractiveDevelopment.mp4
|
||||
|
||||
from manimlib import *
|
||||
|
||||
class InteractiveDevelopment(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
square = Square()
|
||||
|
||||
self.play(ShowCreation(square))
|
||||
self.wait()
|
||||
|
||||
# This opens an iPython termnial 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.
|
||||
self.embed()
|
||||
|
||||
# Try copying and pasting some of the lines below into
|
||||
# the interactive shell
|
||||
self.play(ReplacementTransform(square, circle))
|
||||
self.wait()
|
||||
self.play(circle.animate.stretch(4, 0))
|
||||
self.play(Rotate(circle, 90 * DEGREES))
|
||||
self.play(circle.animate.shift(2 * RIGHT).scale(0.25))
|
||||
|
||||
text = Text("""
|
||||
In general, using the interactive shell
|
||||
is very helpful when developing new scenes
|
||||
""")
|
||||
self.play(Write(text))
|
||||
|
||||
# In the interactive shell, you can just type
|
||||
# play, add, remove, clear, wait, save_state and restore,
|
||||
# instead of self.play, self.add, self.remove, etc.
|
||||
|
||||
# To interact with the window, type touch(). You can then
|
||||
# scroll in the window, or zoom by holding down 'z' while scrolling,
|
||||
# and change camera perspective by holding down 'd' while moving
|
||||
# the mouse. Press 'r' to reset to the standard camera position.
|
||||
# Press 'q' to stop interacting with the window and go back to
|
||||
# typing new commands into the shell.
|
||||
|
||||
# In principle you can customize a scene to be responsive to
|
||||
# mouse and keyboard interactions
|
||||
always(circle.move_to, self.mouse_point)
|
||||
|
||||
This scene is similar to what we wrote in :doc:`quickstart`.
|
||||
And how to interact has been written in the comments.
|
||||
No more explanation here.
|
||||
|
||||
AnimatingMethods
|
||||
----------------
|
||||
|
||||
.. manim-example:: AnimatingMethods
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/AnimatingMethods.mp4
|
||||
|
||||
class AnimatingMethods(Scene):
|
||||
def construct(self):
|
||||
grid = Tex(r"\pi").get_grid(10, 10, height=4)
|
||||
self.add(grid)
|
||||
|
||||
# You can animate the application of mobject methods with the
|
||||
# ".animate" syntax:
|
||||
self.play(grid.animate.shift(LEFT))
|
||||
|
||||
# Alternatively, you can use the older syntax by passing the
|
||||
# method and then the arguments to the scene's "play" function:
|
||||
self.play(grid.shift, LEFT)
|
||||
|
||||
# Both of those will interpolate between the mobject's initial
|
||||
# state and whatever happens when you apply that method.
|
||||
# For this example, calling grid.shift(LEFT) would shift the
|
||||
# grid one unit to the left, but both of the previous calls to
|
||||
# "self.play" animate that motion.
|
||||
|
||||
# The same applies for any method, including those setting colors.
|
||||
self.play(grid.animate.set_color(YELLOW))
|
||||
self.wait()
|
||||
self.play(grid.animate.set_submobject_colors_by_gradient(BLUE, GREEN))
|
||||
self.wait()
|
||||
self.play(grid.animate.set_height(TAU - MED_SMALL_BUFF))
|
||||
self.wait()
|
||||
|
||||
# The method Mobject.apply_complex_function lets you apply arbitrary
|
||||
# complex functions, treating the points defining the mobject as
|
||||
# complex numbers.
|
||||
self.play(grid.animate.apply_complex_function(np.exp), run_time=5)
|
||||
self.wait()
|
||||
|
||||
# Even more generally, you could apply Mobject.apply_function,
|
||||
# which takes in functions form R^3 to R^3
|
||||
self.play(
|
||||
grid.animate.apply_function(
|
||||
lambda p: [
|
||||
p[0] + 0.5 * math.sin(p[1]),
|
||||
p[1] + 0.5 * math.sin(p[0]),
|
||||
p[2]
|
||||
]
|
||||
),
|
||||
run_time=5,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
The new usage in this scene is ``.get_grid()`` and ``self.play(mob.animate.method(args))``.
|
||||
|
||||
- ``.get_grid()`` method will return a new mobject containing multiple copies of this one arranged in a grid.
|
||||
- ``self.play(mob.animate.method(args))`` animates the method, and the details are in the comments above.
|
||||
|
||||
TextExample
|
||||
-----------
|
||||
|
||||
.. manim-example:: TextExample
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/TextExample.mp4
|
||||
|
||||
class TextExample(Scene):
|
||||
def construct(self):
|
||||
# To run this scene properly, you should have "Consolas" font in your computer
|
||||
# for full usage, you can see https://github.com/3b1b/manim/pull/680
|
||||
text = Text("Here is a text", font="Consolas", font_size=90)
|
||||
difference = Text(
|
||||
"""
|
||||
The most important difference between Text and TexText is that\n
|
||||
you can change the font more easily, but can't use the LaTeX grammar
|
||||
""",
|
||||
font="Arial", font_size=24,
|
||||
# t2c is a dict that you can choose color for different text
|
||||
t2c={"Text": BLUE, "TexText": BLUE, "LaTeX": ORANGE}
|
||||
)
|
||||
VGroup(text, difference).arrange(DOWN, buff=1)
|
||||
self.play(Write(text))
|
||||
self.play(FadeIn(difference, UP))
|
||||
self.wait(3)
|
||||
|
||||
fonts = Text(
|
||||
"And you can also set the font according to different words",
|
||||
font="Arial",
|
||||
t2f={"font": "Consolas", "words": "Consolas"},
|
||||
t2c={"font": BLUE, "words": GREEN}
|
||||
)
|
||||
fonts.set_width(FRAME_WIDTH - 1)
|
||||
slant = Text(
|
||||
"And the same as slant and weight",
|
||||
font="Consolas",
|
||||
t2s={"slant": ITALIC},
|
||||
t2w={"weight": BOLD},
|
||||
t2c={"slant": ORANGE, "weight": RED}
|
||||
)
|
||||
VGroup(fonts, slant).arrange(DOWN, buff=0.8)
|
||||
self.play(FadeOut(text), FadeOut(difference, shift=DOWN))
|
||||
self.play(Write(fonts))
|
||||
self.wait()
|
||||
self.play(Write(slant))
|
||||
self.wait()
|
||||
|
||||
The new classes in this scene are ``Text``, ``VGroup``, ``Write``, ``FadeIn`` and ``FadeOut``.
|
||||
|
||||
- ``Text`` can create text, define fonts, etc. The usage ais clearly reflected in the above examples.
|
||||
- ``VGroup`` can put multiple ``VMobject`` together as a whole. In the example, the ``.arrange()`` method is called to arrange the sub-mobjects in sequence downward (``DOWN``), and the spacing is ``buff``.
|
||||
- ``Write`` is an animation that shows similar writing effects.
|
||||
- ``FadeIn`` fades the object in, the second parameter indicates the direction of the fade in.
|
||||
- ``FadeOut`` fades out the object, the second parameter indicates the direction of the fade out.
|
||||
|
||||
TexTransformExample
|
||||
-------------------
|
||||
|
||||
.. manim-example:: TexTransformExample
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/TexTransformExample.mp4
|
||||
|
||||
class TexTransformExample(Scene):
|
||||
def construct(self):
|
||||
to_isolate = ["B", "C", "=", "(", ")"]
|
||||
lines = VGroup(
|
||||
# Passing in muliple arguments to Tex will result
|
||||
# in the same expression as if those arguments had
|
||||
# been joined together, except that the submobject
|
||||
# hierarchy of the resulting mobject ensure that the
|
||||
# Tex mobject has a subject corresponding to
|
||||
# each of these strings. For example, the Tex mobject
|
||||
# below will have 5 subjects, corresponding to the
|
||||
# expressions [A^2, +, B^2, =, C^2]
|
||||
Tex("A^2", "+", "B^2", "=", "C^2"),
|
||||
# Likewise here
|
||||
Tex("A^2", "=", "C^2", "-", "B^2"),
|
||||
# Alternatively, you can pass in the keyword argument
|
||||
# "isolate" with a list of strings that should be out as
|
||||
# their own submobject. So the line below is equivalent
|
||||
# to the commented out line below it.
|
||||
Tex("A^2 = (C + B)(C - B)", isolate=["A^2", *to_isolate]),
|
||||
# Tex("A^2", "=", "(", "C", "+", "B", ")", "(", "C", "-", "B", ")"),
|
||||
Tex("A = \\sqrt{(C + B)(C - B)}", isolate=["A", *to_isolate])
|
||||
)
|
||||
lines.arrange(DOWN, buff=LARGE_BUFF)
|
||||
for line in lines:
|
||||
line.set_color_by_tex_to_color_map({
|
||||
"A": BLUE,
|
||||
"B": TEAL,
|
||||
"C": GREEN,
|
||||
})
|
||||
|
||||
play_kw = {"run_time": 2}
|
||||
self.add(lines[0])
|
||||
# The animation TransformMatchingTex will line up parts
|
||||
# of the source and target which have matching tex strings.
|
||||
# Here, giving it a little path_arc makes each part sort of
|
||||
# rotate into their final positions, which feels appropriate
|
||||
# for the idea of rearranging an equation
|
||||
self.play(
|
||||
TransformMatchingTex(
|
||||
lines[0].copy(), lines[1],
|
||||
path_arc=90 * DEGREES,
|
||||
),
|
||||
**play_kw
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# Now, we could try this again on the next line...
|
||||
self.play(
|
||||
TransformMatchingTex(lines[1].copy(), lines[2]),
|
||||
**play_kw
|
||||
)
|
||||
self.wait()
|
||||
# ...and this looks nice enough, but since there's no tex
|
||||
# in lines[2] which matches "C^2" or "B^2", those terms fade
|
||||
# out to nothing while the C and B terms fade in from nothing.
|
||||
# If, however, we want the C^2 to go to C, and B^2 to go to B,
|
||||
# we can specify that with a key map.
|
||||
self.play(FadeOut(lines[2]))
|
||||
self.play(
|
||||
TransformMatchingTex(
|
||||
lines[1].copy(), lines[2],
|
||||
key_map={
|
||||
"C^2": "C",
|
||||
"B^2": "B",
|
||||
}
|
||||
),
|
||||
**play_kw
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# And to finish off, a simple TransformMatchingShapes would work
|
||||
# just fine. But perhaps we want that exponent on A^2 to transform into
|
||||
# the square root symbol. At the moment, lines[2] treats the expression
|
||||
# A^2 as a unit, so we might create a new version of the same line which
|
||||
# separates out just the A. This way, when TransformMatchingTex lines up
|
||||
# all matching parts, the only mismatch will be between the "^2" from
|
||||
# new_line2 and the "\sqrt" from the final line. By passing in,
|
||||
# transform_mismatches=True, it will transform this "^2" part into
|
||||
# the "\sqrt" part.
|
||||
new_line2 = Tex("A^2 = (C + B)(C - B)", isolate=["A", *to_isolate])
|
||||
new_line2.replace(lines[2])
|
||||
new_line2.match_style(lines[2])
|
||||
|
||||
self.play(
|
||||
TransformMatchingTex(
|
||||
new_line2, lines[3],
|
||||
transform_mismatches=True,
|
||||
),
|
||||
**play_kw
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(FadeOut(lines, RIGHT))
|
||||
|
||||
# Alternatively, if you don't want to think about breaking up
|
||||
# the tex strings deliberately, you can TransformMatchingShapes,
|
||||
# which will try to line up all pieces of a source mobject with
|
||||
# those of a target, regardless of the submobject hierarchy in
|
||||
# each one, according to whether those pieces have the same
|
||||
# shape (as best it can).
|
||||
source = Text("the morse code", height=1)
|
||||
target = Text("here come dots", height=1)
|
||||
|
||||
self.play(Write(source))
|
||||
self.wait()
|
||||
kw = {"run_time": 3, "path_arc": PI / 2}
|
||||
self.play(TransformMatchingShapes(source, target, **kw))
|
||||
self.wait()
|
||||
self.play(TransformMatchingShapes(target, source, **kw))
|
||||
self.wait()
|
||||
|
||||
The new classes in this scene are ``Tex``, ``TexText``, ``TransformMatchingTex``
|
||||
and ``TransformMatchingShapes``.
|
||||
|
||||
- ``Tex`` uses LaTeX to create mathematical formulas.
|
||||
- ``TexText`` uses LaTeX to create text.
|
||||
- ``TransformMatchingTeX`` automatically transforms sub-objects according to the similarities and differences of tex in ``Tex``.
|
||||
- ``TransformMatchingShapes`` automatically transform sub-objects directly based on the similarities and differences of the object point sets.
|
||||
|
||||
UpdatersExample
|
||||
---------------
|
||||
|
||||
.. manim-example:: UpdatersExample
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/UpdatersExample.mp4
|
||||
|
||||
class UpdatersExample(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
square.set_fill(BLUE_E, 1)
|
||||
|
||||
# On all all frames, the constructor Brace(square, UP) will
|
||||
# be called, and the mobject brace will set its data to match
|
||||
# that of the newly constructed object
|
||||
brace = always_redraw(Brace, square, UP)
|
||||
|
||||
text, number = label = VGroup(
|
||||
Text("Width = "),
|
||||
DecimalNumber(
|
||||
0,
|
||||
show_ellipsis=True,
|
||||
num_decimal_places=2,
|
||||
include_sign=True,
|
||||
)
|
||||
)
|
||||
label.arrange(RIGHT)
|
||||
|
||||
# This ensures that the method deicmal.next_to(square)
|
||||
# is called on every frame
|
||||
always(label.next_to, brace, UP)
|
||||
# You could also write the following equivalent line
|
||||
# label.add_updater(lambda m: m.next_to(brace, UP))
|
||||
|
||||
# If the argument itself might change, you can use f_always,
|
||||
# for which the arguments following the initial Mobject method
|
||||
# should be functions returning arguments to that method.
|
||||
# The following line ensures that decimal.set_value(square.get_y())
|
||||
# is called every frame
|
||||
f_always(number.set_value, square.get_width)
|
||||
# You could also write the following equivalent line
|
||||
# number.add_updater(lambda m: m.set_value(square.get_width()))
|
||||
|
||||
self.add(square, brace, label)
|
||||
|
||||
# Notice that the brace and label track with the square
|
||||
self.play(
|
||||
square.animate.scale(2),
|
||||
rate_func=there_and_back,
|
||||
run_time=2,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
square.animate.set_width(5, stretch=True),
|
||||
run_time=3,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
square.animate.set_width(2),
|
||||
run_time=3
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# In general, you can alway call Mobject.add_updater, and pass in
|
||||
# a function that you want to be called on every frame. The function
|
||||
# should take in either one argument, the mobject, or two arguments,
|
||||
# the mobject and the amount of time since the last frame.
|
||||
now = self.time
|
||||
w0 = square.get_width()
|
||||
square.add_updater(
|
||||
lambda m: m.set_width(w0 * math.cos(self.time - now))
|
||||
)
|
||||
self.wait(4 * PI)
|
||||
|
||||
The new classes and usage in this scene are ``always_redraw()``, ``DecimalNumber``, ``.to_edge()``,
|
||||
``.center()``, ``always()``, ``f_always()``, ``.set_y()`` and ``.add_updater()``.
|
||||
|
||||
- ``always_redraw()`` function create a new mobject every frame.
|
||||
- ``DecimalNumber`` is a variable number, speed it up by breaking it into ``Text`` characters.
|
||||
- ``.to_edge()`` means to place the object on the edge of the screen.
|
||||
- ``.center()`` means to place the object in the center of the screen.
|
||||
- ``always(f, x)`` means that a certain function (``f(x)``) is executed every frame.
|
||||
- ``f_always(f, g)`` is similar to ``always``, executed ``f(g())`` every frame.
|
||||
- ``.set_y()`` means to set the ordinate of the object on the screen.
|
||||
- ``.add_updater()`` sets an update function for the object. For example: ``mob1.add_updater(lambda mob: mob.next_to(mob2))`` means ``mob1.next_to(mob2)`` is executed every frame.
|
||||
|
||||
CoordinateSystemExample
|
||||
-----------------------
|
||||
|
||||
.. manim-example:: CoordinateSystemExample
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/CoordinateSystemExample.mp4
|
||||
|
||||
class CoordinateSystemExample(Scene):
|
||||
def construct(self):
|
||||
axes = Axes(
|
||||
# x-axis ranges from -1 to 10, with a default step size of 1
|
||||
x_range=(-1, 10),
|
||||
# y-axis ranges from -2 to 2 with a step size of 0.5
|
||||
y_range=(-2, 2, 0.5),
|
||||
# The axes will be stretched so as to match the specified
|
||||
# height and width
|
||||
height=6,
|
||||
width=10,
|
||||
# Axes is made of two NumberLine mobjects. You can specify
|
||||
# their configuration with axis_config
|
||||
axis_config={
|
||||
"stroke_color": GREY_A,
|
||||
"stroke_width": 2,
|
||||
},
|
||||
# Alternatively, you can specify configuration for just one
|
||||
# of them, like this.
|
||||
y_axis_config={
|
||||
"include_tip": False,
|
||||
}
|
||||
)
|
||||
# Keyword arguments of add_coordinate_labels can be used to
|
||||
# configure the DecimalNumber mobjects which it creates and
|
||||
# adds to the axes
|
||||
axes.add_coordinate_labels(
|
||||
font_size=20,
|
||||
num_decimal_places=1,
|
||||
)
|
||||
self.add(axes)
|
||||
|
||||
# Axes descends from the CoordinateSystem class, meaning
|
||||
# you can call call axes.coords_to_point, abbreviated to
|
||||
# axes.c2p, to associate a set of coordinates with a point,
|
||||
# like so:
|
||||
dot = Dot(color=RED)
|
||||
dot.move_to(axes.c2p(0, 0))
|
||||
self.play(FadeIn(dot, scale=0.5))
|
||||
self.play(dot.animate.move_to(axes.c2p(3, 2)))
|
||||
self.wait()
|
||||
self.play(dot.animate.move_to(axes.c2p(5, 0.5)))
|
||||
self.wait()
|
||||
|
||||
# Similarly, you can call axes.point_to_coords, or axes.p2c
|
||||
# print(axes.p2c(dot.get_center()))
|
||||
|
||||
# We can draw lines from the axes to better mark the coordinates
|
||||
# of a given point.
|
||||
# Here, the always_redraw command means that on each new frame
|
||||
# the lines will be redrawn
|
||||
h_line = always_redraw(lambda: axes.get_h_line(dot.get_left()))
|
||||
v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom()))
|
||||
|
||||
self.play(
|
||||
ShowCreation(h_line),
|
||||
ShowCreation(v_line),
|
||||
)
|
||||
self.play(dot.animate.move_to(axes.c2p(3, -2)))
|
||||
self.wait()
|
||||
self.play(dot.animate.move_to(axes.c2p(1, 1)))
|
||||
self.wait()
|
||||
|
||||
# If we tie the dot to a particular set of coordinates, notice
|
||||
# that as we move the axes around it respects the coordinate
|
||||
# system defined by them.
|
||||
f_always(dot.move_to, lambda: axes.c2p(1, 1))
|
||||
self.play(
|
||||
axes.animate.scale(0.75).to_corner(UL),
|
||||
run_time=2,
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeOut(VGroup(axes, dot, h_line, v_line)))
|
||||
|
||||
# Other coordinate systems you can play around with include
|
||||
# ThreeDAxes, NumberPlane, and ComplexPlane.
|
||||
|
||||
|
||||
GraphExample
|
||||
------------
|
||||
|
||||
.. manim-example:: GraphExample
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/GraphExample.mp4
|
||||
|
||||
class GraphExample(Scene):
|
||||
def construct(self):
|
||||
axes = Axes((-3, 10), (-1, 8))
|
||||
axes.add_coordinate_labels()
|
||||
|
||||
self.play(Write(axes, lag_ratio=0.01, run_time=1))
|
||||
|
||||
# Axes.get_graph will return the graph of a function
|
||||
sin_graph = axes.get_graph(
|
||||
lambda x: 2 * math.sin(x),
|
||||
color=BLUE,
|
||||
)
|
||||
# By default, it draws it so as to somewhat smoothly interpolate
|
||||
# between sampled points (x, f(x)). If the graph is meant to have
|
||||
# a corner, though, you can set use_smoothing to False
|
||||
relu_graph = axes.get_graph(
|
||||
lambda x: max(x, 0),
|
||||
use_smoothing=False,
|
||||
color=YELLOW,
|
||||
)
|
||||
# For discontinuous functions, you can specify the point of
|
||||
# discontinuity so that it does not try to draw over the gap.
|
||||
step_graph = axes.get_graph(
|
||||
lambda x: 2.0 if x > 3 else 1.0,
|
||||
discontinuities=[3],
|
||||
color=GREEN,
|
||||
)
|
||||
|
||||
# Axes.get_graph_label takes in either a string or a mobject.
|
||||
# If it's a string, it treats it as a LaTeX expression. By default
|
||||
# it places the label next to the graph near the right side, and
|
||||
# has it match the color of the graph
|
||||
sin_label = axes.get_graph_label(sin_graph, "\\sin(x)")
|
||||
relu_label = axes.get_graph_label(relu_graph, Text("ReLU"))
|
||||
step_label = axes.get_graph_label(step_graph, Text("Step"), x=4)
|
||||
|
||||
self.play(
|
||||
ShowCreation(sin_graph),
|
||||
FadeIn(sin_label, RIGHT),
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
ReplacementTransform(sin_graph, relu_graph),
|
||||
FadeTransform(sin_label, relu_label),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
ReplacementTransform(relu_graph, step_graph),
|
||||
FadeTransform(relu_label, step_label),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
parabola = axes.get_graph(lambda x: 0.25 * x**2)
|
||||
parabola.set_stroke(BLUE)
|
||||
self.play(
|
||||
FadeOut(step_graph),
|
||||
FadeOut(step_label),
|
||||
ShowCreation(parabola)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# You can use axes.input_to_graph_point, abbreviated
|
||||
# to axes.i2gp, to find a particular point on a graph
|
||||
dot = Dot(color=RED)
|
||||
dot.move_to(axes.i2gp(2, parabola))
|
||||
self.play(FadeIn(dot, scale=0.5))
|
||||
|
||||
# A value tracker lets us animate a parameter, usually
|
||||
# with the intent of having other mobjects update based
|
||||
# on the parameter
|
||||
x_tracker = ValueTracker(2)
|
||||
f_always(
|
||||
dot.move_to,
|
||||
lambda: axes.i2gp(x_tracker.get_value(), parabola)
|
||||
)
|
||||
|
||||
self.play(x_tracker.animate.set_value(4), run_time=3)
|
||||
self.play(x_tracker.animate.set_value(-2), run_time=3)
|
||||
self.wait()
|
||||
|
||||
SurfaceExample
|
||||
--------------
|
||||
|
||||
.. manim-example:: SurfaceExample
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/SurfaceExample.mp4
|
||||
|
||||
class SurfaceExample(Scene):
|
||||
CONFIG = {
|
||||
"camera_class": ThreeDCamera,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
surface_text = Text("For 3d scenes, try using surfaces")
|
||||
surface_text.fix_in_frame()
|
||||
surface_text.to_edge(UP)
|
||||
self.add(surface_text)
|
||||
self.wait(0.1)
|
||||
|
||||
torus1 = Torus(r1=1, r2=1)
|
||||
torus2 = Torus(r1=3, r2=1)
|
||||
sphere = Sphere(radius=3, resolution=torus1.resolution)
|
||||
# You can texture a surface with up to two images, which will
|
||||
# be interpreted as the side towards the light, and away from
|
||||
# the light. These can be either urls, or paths to a local file
|
||||
# in whatever you've set as the image directory in
|
||||
# the custom_config.yml file
|
||||
|
||||
# day_texture = "EarthTextureMap"
|
||||
# night_texture = "NightEarthTextureMap"
|
||||
day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
|
||||
night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
|
||||
|
||||
surfaces = [
|
||||
TexturedSurface(surface, day_texture, night_texture)
|
||||
for surface in [sphere, torus1, torus2]
|
||||
]
|
||||
|
||||
for mob in surfaces:
|
||||
mob.shift(IN)
|
||||
mob.mesh = SurfaceMesh(mob)
|
||||
mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
|
||||
|
||||
# Set perspective
|
||||
frame = self.camera.frame
|
||||
frame.set_euler_angles(
|
||||
theta=-30 * DEGREES,
|
||||
phi=70 * DEGREES,
|
||||
)
|
||||
|
||||
surface = surfaces[0]
|
||||
|
||||
self.play(
|
||||
FadeIn(surface),
|
||||
ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3),
|
||||
)
|
||||
for mob in surfaces:
|
||||
mob.add(mob.mesh)
|
||||
surface.save_state()
|
||||
self.play(Rotate(surface, PI / 2), run_time=2)
|
||||
for mob in surfaces[1:]:
|
||||
mob.rotate(PI / 2)
|
||||
|
||||
self.play(
|
||||
Transform(surface, surfaces[1]),
|
||||
run_time=3
|
||||
)
|
||||
|
||||
self.play(
|
||||
Transform(surface, surfaces[2]),
|
||||
# Move camera frame during the transition
|
||||
frame.animate.increment_phi(-10 * DEGREES),
|
||||
frame.animate.increment_theta(-20 * DEGREES),
|
||||
run_time=3
|
||||
)
|
||||
# Add ambient rotation
|
||||
frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
|
||||
|
||||
# Play around with where the light is
|
||||
light_text = Text("You can move around the light source")
|
||||
light_text.move_to(surface_text)
|
||||
light_text.fix_in_frame()
|
||||
|
||||
self.play(FadeTransform(surface_text, light_text))
|
||||
light = self.camera.light_source
|
||||
self.add(light)
|
||||
light.save_state()
|
||||
self.play(light.animate.move_to(3 * IN), run_time=5)
|
||||
self.play(light.animate.shift(10 * OUT), run_time=5)
|
||||
|
||||
drag_text = Text("Try moving the mouse while pressing d or s")
|
||||
drag_text.move_to(light_text)
|
||||
drag_text.fix_in_frame()
|
||||
|
||||
self.play(FadeTransform(light_text, drag_text))
|
||||
self.wait()
|
||||
|
||||
This scene shows an example of using a three-dimensional surface, and
|
||||
the related usage has been briefly described in the notes.
|
||||
|
||||
- ``.fix_in_frame()`` makes the object not change with the view angle of the screen, and is always displayed at a fixed position on the screen.
|
||||
|
||||
OpeningManimExample
|
||||
-------------------
|
||||
|
||||
.. manim-example:: OpeningManimExample
|
||||
:media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/OpeningManimExample.mp4
|
||||
|
||||
|
||||
class OpeningManimExample(Scene):
|
||||
def construct(self):
|
||||
intro_words = Text("""
|
||||
The original motivation for manim was to
|
||||
better illustrate mathematical functions
|
||||
as transformations.
|
||||
""")
|
||||
intro_words.to_edge(UP)
|
||||
|
||||
self.play(Write(intro_words))
|
||||
self.wait(2)
|
||||
|
||||
# Linear transform
|
||||
grid = NumberPlane((-10, 10), (-5, 5))
|
||||
matrix = [[1, 1], [0, 1]]
|
||||
linear_transform_words = VGroup(
|
||||
Text("This is what the matrix"),
|
||||
IntegerMatrix(matrix, include_background_rectangle=True),
|
||||
Text("looks like")
|
||||
)
|
||||
linear_transform_words.arrange(RIGHT)
|
||||
linear_transform_words.to_edge(UP)
|
||||
linear_transform_words.set_stroke(BLACK, 10, background=True)
|
||||
|
||||
self.play(
|
||||
ShowCreation(grid),
|
||||
FadeTransform(intro_words, linear_transform_words)
|
||||
)
|
||||
self.wait()
|
||||
self.play(grid.animate.apply_matrix(matrix), run_time=3)
|
||||
self.wait()
|
||||
|
||||
# Complex map
|
||||
c_grid = ComplexPlane()
|
||||
moving_c_grid = c_grid.copy()
|
||||
moving_c_grid.prepare_for_nonlinear_transform()
|
||||
c_grid.set_stroke(BLUE_E, 1)
|
||||
c_grid.add_coordinate_labels(font_size=24)
|
||||
complex_map_words = TexText("""
|
||||
Or thinking of the plane as $\\mathds{C}$,\\\\
|
||||
this is the map $z \\rightarrow z^2$
|
||||
""")
|
||||
complex_map_words.to_corner(UR)
|
||||
complex_map_words.set_stroke(BLACK, 5, background=True)
|
||||
|
||||
self.play(
|
||||
FadeOut(grid),
|
||||
Write(c_grid, run_time=3),
|
||||
FadeIn(moving_c_grid),
|
||||
FadeTransform(linear_transform_words, complex_map_words),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
moving_c_grid.animate.apply_complex_function(lambda z: z**2),
|
||||
run_time=6,
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
This scene is a comprehensive application of a two-dimensional scene.
|
||||
|
||||
After seeing these scenes, you have already understood part of the
|
||||
usage of manim. For more examples, see `the video code of 3b1b <https://github.com/3b1b/videos>`_.
|
||||
69
docs/source/getting_started/installation.rst
Normal file
69
docs/source/getting_started/installation.rst
Normal file
@@ -0,0 +1,69 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
Manim runs on Python 3.6 or higher (Python 3.8 is recommended).
|
||||
|
||||
System requirements are:
|
||||
|
||||
- `FFmpeg <https://ffmpeg.org/>`__
|
||||
- `OpenGL <https://www.opengl.org//>`__ (included in python package ``PyOpenGL``)
|
||||
- `LaTeX <https://www.latex-project.org>`__ (optional, if you want to use LaTeX)
|
||||
- `Pango <https://pango.org>`__ (only for Linux)
|
||||
|
||||
|
||||
Directly
|
||||
--------
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# Install manimgl
|
||||
pip install manimgl
|
||||
|
||||
# Try it out
|
||||
manimgl
|
||||
|
||||
If you want to hack on manimlib itself, clone this repository and in
|
||||
that directory execute:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# Install python requirements
|
||||
pip install -e .
|
||||
|
||||
# Try it out
|
||||
manimgl example_scenes.py OpeningManimExample
|
||||
# or
|
||||
manim-render example_scenes.py OpeningManimExample
|
||||
|
||||
If you run the above command and no error message appears,
|
||||
then you have successfully installed all the environments required by manim.
|
||||
|
||||
Directly (Windows)
|
||||
------------------
|
||||
|
||||
1. `Install
|
||||
FFmpeg <https://www.wikihow.com/Install-FFmpeg-on-Windows>`__, and make sure that its path is in the PATH environment variable.
|
||||
2. Install a LaTeX distribution.
|
||||
`TeXLive-full <http://tug.org/texlive/>`__ is recommended.
|
||||
3. Install the remaining Python packages.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
git clone https://github.com/3b1b/manim.git
|
||||
cd manim
|
||||
pip install -e .
|
||||
manimgl example_scenes.py OpeningManimExample
|
||||
|
||||
For Anaconda
|
||||
------------
|
||||
|
||||
- Install FFmpeg and LaTeX as above.
|
||||
- Create a conda environment using
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
git clone https://github.com/3b1b/manim.git
|
||||
cd manim
|
||||
conda create -n manim python=3.8
|
||||
conda activate manim
|
||||
pip install -e .
|
||||
256
docs/source/getting_started/quickstart.rst
Normal file
256
docs/source/getting_started/quickstart.rst
Normal file
@@ -0,0 +1,256 @@
|
||||
Quick Start
|
||||
===========
|
||||
|
||||
After installing the manim environment according to the instructions on the
|
||||
:doc:`installation` page, you can try to make a scene yourself from scratch.
|
||||
|
||||
First, create a new ``.py`` file (such as ``start.py``) according to the following
|
||||
directory structure:
|
||||
|
||||
.. code-block:: text
|
||||
:emphasize-lines: 8
|
||||
|
||||
manim/
|
||||
├── manimlib/
|
||||
│ ├── animation/
|
||||
│ ├── ...
|
||||
│ ├── default_config.yml
|
||||
│ └── window.py
|
||||
├── custom_config.yml
|
||||
└── start.py
|
||||
|
||||
And paste the following code (I will explain the function of each line in detail later):
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from manimlib import *
|
||||
|
||||
class SquareToCircle(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
|
||||
self.add(circle)
|
||||
|
||||
And run this command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
manimgl start.py SquareToCircle
|
||||
|
||||
A window will pop up on the screen. And then you can :
|
||||
|
||||
- scroll the middle mouse button to move the screen up and down
|
||||
- hold down the :kbd:`z` on the keyboard while scrolling the middle mouse button to zoom the screen
|
||||
- hold down the :kbd:`s` key on the keyboard and move the mouse to pan the screen
|
||||
- hold down the :kbd:`d` key on the keyboard and move the mouse to change the three-dimensional perspective.
|
||||
|
||||
Finally, you can close the window and exit the program by pressing :kbd:`q`.
|
||||
|
||||
Run this command again:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
manimgl start.py SquareToCircle -os
|
||||
|
||||
At this time, no window will pop up. When the program is finished, this rendered
|
||||
image will be automatically opened (saved in the subdirectory ``images/`` of the same
|
||||
level directory of ``start.py`` by default):
|
||||
|
||||
.. image:: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/quickstart/SquareToCircle.png
|
||||
:align: center
|
||||
|
||||
Make an image
|
||||
-------------
|
||||
|
||||
Next, let's take a detailed look at what each row does.
|
||||
|
||||
**Line 1**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from manimlib import *
|
||||
|
||||
This will import all the classes that may be used when using manim.
|
||||
|
||||
**Line 3**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class SquareToCircle(Scene):
|
||||
|
||||
Create a :class:`Scene` subclass ``SquareToCircle``, which will be
|
||||
the scene you write and render.
|
||||
|
||||
**Line 4**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def construct(self):
|
||||
|
||||
Write the ``construct()`` method, the content of which will determine
|
||||
how to create the mobjects in the screen and what operations need to be performed.
|
||||
|
||||
**Line 5**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
circle = Circle()
|
||||
|
||||
Create a circle (an instance of the :class:`Circle` class), called ``circle``
|
||||
|
||||
**Line 6~7**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
|
||||
Set the circle style by calling the circle's method.
|
||||
|
||||
- The ``.set_fill()`` method sets the fill color of this circle to blue (``BLUE``, defined in :doc:`../documentation/constants`), and the fill transparency to 0.5.
|
||||
- The ``.set_stroke()`` method sets the stroke color of this circle to dark blue (``BLUE_E``, defined in :doc:`../documentation/constants`), and the stroke width to 4.
|
||||
|
||||
**Line 9**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.add(circle)
|
||||
|
||||
Add this circle to the screen through the ``.add()`` method of :class:`Scene`.
|
||||
|
||||
Add animations
|
||||
--------------
|
||||
|
||||
Let's change some codes and add some animations to make videos instead of just pictures.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from manimlib import *
|
||||
|
||||
class SquareToCircle(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
square = Square()
|
||||
|
||||
self.play(ShowCreation(square))
|
||||
self.wait()
|
||||
self.play(ReplacementTransform(square, circle))
|
||||
self.wait()
|
||||
|
||||
Run this command this time:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
manimgl start.py SquareToCircle
|
||||
|
||||
The pop-up window will play animations of drawing a square and transforming
|
||||
it into a circle. If you want to save this video, run:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
manimgl start.py SquareToCircle -o
|
||||
|
||||
This time there will be no pop-up window, but the video file (saved in the subdirectory
|
||||
``videos/`` of the same level directory of ``start.py`` by default) will be automatically
|
||||
opened after the operation is over:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video class="manim-video" controls loop autoplay src="https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/quickstart/SquareToCircle.mp4"></video>
|
||||
|
||||
Let's take a look at the code this time. The first 7 lines are the same as the previous
|
||||
ones, and the 8th line is similar to the 5th line, which creates an instance of the
|
||||
:class:`Square` class and named it ``square``.
|
||||
|
||||
**Line 10**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.play(ShowCreation(square))
|
||||
|
||||
An animation is played through :class:`Scene`'s ``.play()`` method. :class:`ShowCreation`
|
||||
is an animation that shows the process of creating a given mobject.
|
||||
``self.play(ShowCreation(square))`` is to play the animation of creating ``square``.
|
||||
|
||||
**Line 11**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.wait()
|
||||
|
||||
Use :class:`Scene`'s ``.wait()`` method to pause (default 1s), you can pass in
|
||||
parameters to indicate the pause time (for example, ``self.wait(3)`` means pause for 3s).
|
||||
|
||||
**Line 12**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.play(ReplacementTransform(square, circle))
|
||||
|
||||
Play the animation that transforms ``square`` into ``circle``.
|
||||
``ReplacementTransform(A, B)`` means to transform A into B's pattern and replace A with B.
|
||||
|
||||
**Line 13**: Same as line 11, pause for 1s.
|
||||
|
||||
|
||||
Enable interaction
|
||||
------------------
|
||||
|
||||
Interaction is a new feature of the new version. You can add the following line
|
||||
at the end of the code to enable interaction:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.embed()
|
||||
|
||||
Then run ``manimgl start.py SquareToCircle``.
|
||||
|
||||
After the previous animation is executed, the ipython terminal will be opened on
|
||||
the command line. After that, you can continue to write code in it, and the statement
|
||||
you entered will be executed immediately after pressing :kbd:`Enter`.
|
||||
|
||||
For example: input the following lines (without comment lines) into it respectively
|
||||
(``self.play`` can be abbreviated as ``play`` in this mode):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Stretched 4 times in the vertical direction
|
||||
play(circle.animate.stretch(4, dim=0))
|
||||
# Rotate the ellipse 90°
|
||||
play(Rotate(circle, TAU / 4))
|
||||
# Move 2 units to the right and shrink to 1/4 of the original
|
||||
play(circle.animate.shift(2 * RIGHT), circle.animate.scale(0.25))
|
||||
# Insert 10 curves into circle for non-linear transformation (no animation will play)
|
||||
circle.insert_n_curves(10)
|
||||
# Apply a complex transformation of f(z)=z^2 to all points on the circle
|
||||
play(circle.animate.apply_complex_function(lambda z: z**2))
|
||||
# Close the window and exit the program
|
||||
exit()
|
||||
|
||||
You will get an animation similar to the following:
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video class="manim-video" controls loop autoplay src="https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/quickstart/SquareToCircleEmbed.mp4"></video>
|
||||
|
||||
If you want to enter the interactive mode directly, you don't have to write an
|
||||
empty scene containing only ``self.embed()``, you can directly run the following command
|
||||
(this will enter the ipython terminal while the window pops up):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
manimgl
|
||||
|
||||
You succeeded!
|
||||
--------------
|
||||
|
||||
After reading the above content, you already know how to use manim.
|
||||
Below you can see some examples, in the :doc:`example_scenes` page.
|
||||
But before that, you'd better have a look at the :doc:`configuration` of manim.
|
||||
|
||||
128
docs/source/getting_started/structure.rst
Normal file
128
docs/source/getting_started/structure.rst
Normal file
@@ -0,0 +1,128 @@
|
||||
Manim's structure
|
||||
=================
|
||||
|
||||
|
||||
Manim's directory structure
|
||||
---------------------------
|
||||
|
||||
The manim directory looks very complicated, with a lot of files,
|
||||
but the structure is clear.
|
||||
|
||||
Below is the directory structure of manim:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
manimlib/ # manim library
|
||||
├── __init__.py
|
||||
├── __main__.py
|
||||
├── default_config.yml # Default configuration file
|
||||
├── config.py # Process CLI flags
|
||||
├── constants.py # Defined some constants
|
||||
├── extract_scene.py # Extract and run the scene
|
||||
├── shader_wrapper.py # Shaders' Wrapper for convenient control
|
||||
├── window.py # Playback window
|
||||
├── tex_templates/ # Templates preset for LaTeX
|
||||
│ ├── tex_templates.tex # Tex template (will be compiled with latex, default)
|
||||
│ └── ctex_templates.tex # Tex template that support Chinese (will be compiled with xelatex)
|
||||
├── camera/
|
||||
│ └── camera.py # Including Camera and CameraFrame
|
||||
├── scene/
|
||||
│ ├── scene_file_writer.py # Used to write scene to video file
|
||||
│ ├── scene.py # The basic Scene class
|
||||
│ ├── three_d_scene.py # Three-dimensional scene
|
||||
│ ├── sample_space_scene.py # Probability related sample space scene
|
||||
│ └── vector_space_scene.py # Vector field scene
|
||||
├── animation/
|
||||
│ ├── animation.py # The basic class of animation
|
||||
│ ├── composition.py # Animation group
|
||||
│ ├── creation.py # Animation related to Create
|
||||
│ ├── fading.py # Fade related animation
|
||||
│ ├── growing.py # Animation related to Grow
|
||||
│ ├── indication.py # Some animations for emphasis
|
||||
│ ├── movement.py # Animation related to movement
|
||||
│ ├── numbers.py # Realize changes to DecimalNumber
|
||||
│ ├── rotation.py # Animation related to rotation
|
||||
│ ├── specialized.py # Some uncommon animations for special projects
|
||||
│ ├── transform_matching_parts.py # Transform which can automatically match parts
|
||||
│ ├── transform.py # Some Transforms
|
||||
│ └── update.py # Realize update from function
|
||||
├── mobject/
|
||||
│ ├── mobject.py # The basic class of all math object
|
||||
│ ├── types/ # 4 types of mobject
|
||||
│ │ ├── dot_cloud.py # Dot cloud (an subclass of PMobject)
|
||||
│ │ ├── image_mobject.py # Insert pictures
|
||||
│ │ ├── point_cloud_mobject.py # PMobject (mobject composed of points)
|
||||
│ │ ├── surface.py # ParametricSurface
|
||||
│ │ └── vectorized_mobject.py # VMobject (vectorized mobject)
|
||||
│ ├── svg/ # mobject related to svg
|
||||
│ │ ├── svg_mobject.py # SVGMobject
|
||||
│ │ ├── brace.py # Brace
|
||||
│ │ ├── drawings.py # Some special mobject of svg image
|
||||
│ │ ├── tex_mobject.py # Tex and TexText implemented by LaTeX
|
||||
│ │ └── text_mobject.py # Text implemented by manimpango
|
||||
│ ├── changing.py # Dynamically changing mobject
|
||||
│ ├── coordinate_systems.py # coordinate system
|
||||
│ ├── frame.py # mobject related to frame
|
||||
│ ├── functions.py # ParametricFunction
|
||||
│ ├── geometry.py # geometry mobjects
|
||||
│ ├── matrix.py # matrix
|
||||
│ ├── mobject_update_utils.py # some defined updater
|
||||
│ ├── number_line.py # Number line
|
||||
│ ├── numbers.py # Numbers that can be changed
|
||||
│ ├── probability.py # mobject related to probability
|
||||
│ ├── shape_matchers.py # mobject adapted to the size of other objects
|
||||
│ ├── three_dimensions.py # Three-dimensional objects
|
||||
│ ├── value_tracker.py # ValueTracker which storage number
|
||||
│ └── vector_field.py # VectorField
|
||||
├── once_useful_constructs/ # 3b1b's Common scenes written for some videos
|
||||
│ └── ...
|
||||
├── shaders/ # GLSL scripts for rendering
|
||||
│ ├── simple_vert.glsl # a simple glsl script for position
|
||||
│ ├── insert/ # glsl scripts to be inserted in other glsl scripts
|
||||
│ │ ├── NOTE.md # explain how to insert glsl scripts
|
||||
│ │ └── ... # useful scripts
|
||||
│ ├── image/ # glsl for images
|
||||
│ │ └── ... # containing shaders for vertex and fragment
|
||||
│ ├── quadratic_bezier_fill/ # glsl for the fill of quadratic bezier curve
|
||||
│ │ └── ... # containing shaders for vertex, fragment and geometry
|
||||
│ ├── quadratic_bezier_stroke/ # glsl for the stroke of quadratic bezier curve
|
||||
│ │ └── ... # containing shaders for vertex, fragment and geometry
|
||||
│ ├── surface/ # glsl for surfaces
|
||||
│ │ └── ... # containing shaders for vertex and fragment
|
||||
│ ├── textured_surface/ # glsl for textured_surface
|
||||
│ │ └── ... # containing shaders for vertex and fragment
|
||||
│ └── true_dot/ # glsl for a dot
|
||||
│ └── ... # containing shaders for vertex, fragment and geometry
|
||||
└── utils/ # Some useful utility functions
|
||||
├── bezier.py # For bezier curve
|
||||
├── color.py # For color
|
||||
├── config_ops.py # Process CONFIG
|
||||
├── customization.py # Read from custom_config.yml
|
||||
├── debug.py # Utilities for debugging in program
|
||||
├── directories.py # Read directories from config file
|
||||
├── family_ops.py # Process family members
|
||||
├── file_ops.py # Process files and directories
|
||||
├── images.py # Read image
|
||||
├── init_config.py # Configuration guide
|
||||
├── iterables.py # Functions related to list/dictionary processing
|
||||
├── paths.py # Curve path
|
||||
├── rate_functions.py # Some defined rate_functions
|
||||
├── simple_functions.py # Some commonly used functions
|
||||
├── sounds.py # Process sounds
|
||||
├── space_ops.py # Space coordinate calculation
|
||||
├── strings.py # Process strings
|
||||
└── tex_file_writing.py # Use LaTeX to write strings as svg
|
||||
|
||||
Inheritance structure of manim's classes
|
||||
----------------------------------------
|
||||
|
||||
`Here <https://github.com/3b1b/manim/files/5824383/manim_shaders_structure.pdf>`_
|
||||
is a pdf showed inheritance structure of manim's classes, large,
|
||||
but basically all classes have included:
|
||||
|
||||
.. image:: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/manim_shaders_structure.png
|
||||
|
||||
Manim execution process
|
||||
-----------------------
|
||||
|
||||
.. image:: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/manim_shaders_process_en.png
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user