mirror of
https://github.com/3b1b/manim.git
synced 2026-01-12 16:08:16 -05:00
Compare commits
283 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aff2d21bad | ||
|
|
ca0b7a6b06 | ||
|
|
d31be357e5 | ||
|
|
a529a59abf | ||
|
|
cf656e9c21 | ||
|
|
7f1a15b6ba | ||
|
|
dbdd799696 | ||
|
|
522a5b3c5f | ||
|
|
3362f93964 | ||
|
|
af65c9d5d4 | ||
|
|
f0cdcd90ba | ||
|
|
19e3c97589 | ||
|
|
811ee4d26b | ||
|
|
c847988e10 | ||
|
|
1bb2e8c237 | ||
|
|
77ab3b8d3a | ||
|
|
87d8671995 | ||
|
|
f0796055cc | ||
|
|
48f2a1ec3c | ||
|
|
422e86e0da | ||
|
|
c896f58bbd | ||
|
|
97a0a707d7 | ||
|
|
30728879be | ||
|
|
77acc999cd | ||
|
|
1279f040da | ||
|
|
0f71b0a408 | ||
|
|
ada09f1928 | ||
|
|
839fb4ff58 | ||
|
|
24d077fbce | ||
|
|
16f8c025cd | ||
|
|
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 | ||
|
|
4096fc28b8 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,11 +7,13 @@ cairo_test.py
|
||||
mayavi_test.py
|
||||
random_scenes/
|
||||
files/
|
||||
assets/
|
||||
ben_playground.py
|
||||
ben_cairo_test.py
|
||||
.floo
|
||||
.flooignore
|
||||
.vscode
|
||||
.vs
|
||||
*.xml
|
||||
*.iml
|
||||
media
|
||||
@@ -23,4 +25,4 @@ dist/
|
||||
manim.egg-info/
|
||||
|
||||
primes.py
|
||||
/media_dir.txt
|
||||
/media_dir.txt
|
||||
@@ -1,5 +1,4 @@
|
||||
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
|
||||
|
||||
15
Dockerfile
15
Dockerfile
@@ -3,12 +3,17 @@ 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 \
|
||||
texlive \
|
||||
texlive-fonts-extra \
|
||||
texlive-latex-extra \
|
||||
texlive-latex-recommended \
|
||||
texlive-science \
|
||||
tipa \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY requirements.txt requirements.txt
|
||||
RUN python -m pip install -r requirements.txt && rm requirements.txt
|
||||
COPY . /manim
|
||||
RUN cd /manim \
|
||||
&& python setup.py sdist \
|
||||
&& python -m pip install dist/manimlib*
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
|
||||
3
LICENSE
3
LICENSE
@@ -1,5 +1,4 @@
|
||||
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.
|
||||
All files of this project under the directory "from_3b1b" 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:
|
||||
|
||||
|
||||
70
README.md
70
README.md
@@ -1,15 +1,15 @@
|
||||
<img src="logo/cropped.png"/>
|
||||

|
||||
|
||||
[](https://travis-ci.org/3b1b/manim)
|
||||
[](https://www.eulertour.com/learn/manim/)
|
||||
[](https://www.eulertour.com/docs)
|
||||
[](http://choosealicense.com/licenses/mit/)
|
||||
[](https://www.reddit.com/r/manim/)
|
||||
[](https://discord.gg/mMRrZQW)
|
||||
[](https://discord.gg/mMRrZQW)
|
||||
|
||||
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/).
|
||||
|
||||
## Installation
|
||||
Manim runs on python 3.7. You can install it from PyPI via pip
|
||||
Manim runs on Python 3.7. You can install it from PyPI via pip:
|
||||
|
||||
```sh
|
||||
pip3 install manimlib
|
||||
@@ -23,7 +23,7 @@ You can now use it via the `manim` command. For example:
|
||||
manim my_project.py MyScene
|
||||
```
|
||||
|
||||
For more options, take a look at the “Using manim“ sections further below.
|
||||
For more options, take a look at the [Using manim](#using-manim) sections further below.
|
||||
|
||||
### Directly
|
||||
|
||||
@@ -34,12 +34,12 @@ If you want to hack on manimlib itself, clone this repository and in that direct
|
||||
python3 -m pip install -r requirements.txt
|
||||
|
||||
# Try it out
|
||||
python3 -m manim example_scenes.py SquareToCircle -pl
|
||||
python3 ./manim.py example_scenes.py SquareToCircle -pl
|
||||
```
|
||||
|
||||
### 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.
|
||||
2. [Install Cairo](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
|
||||
```
|
||||
@@ -74,14 +74,29 @@ 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.
|
||||
The prebuilt container image has manim repository included.
|
||||
`INPUT_PATH` is where the container looks for scene files. You must set the `INPUT_PATH`
|
||||
environment variable to the absolute path containing your scene file and the
|
||||
`OUTPUT_PATH` environment variable to the directory where you want media to be written.
|
||||
|
||||
1. [Install Docker](https://www.docker.com/products/overview)
|
||||
1. [Install Docker](https://docs.docker.com)
|
||||
2. [Install Docker Compose](https://docs.docker.com/compose/install/)
|
||||
3. Render an animation
|
||||
3. Render an animation:
|
||||
```sh
|
||||
MANIM_PATH=/absolute/path/to/manim/repo docker-compose run manim example_scenes.py SquareToCircle -l
|
||||
INPUT_PATH=/path/to/dir/containing/source/code \
|
||||
OUTPUT_PATH=/path/to/output/ \
|
||||
docker-compose run manim example_scenes.py SquareToCircle -l
|
||||
```
|
||||
The command needs to be run as root if your username is not in the docker group.
|
||||
|
||||
You can replace `example.scenes.py` with any relative path from your `INPUT_PATH`.
|
||||
|
||||

|
||||
|
||||
After running the output will say files ready at `/tmp/output/`, which refers to path inside the container. Your `OUTPUT_PATH` is bind mounted to this `/tmp/output` so any changes made by the container to `/tmp/output` will be mirrored on your `OUTPUT_PATH`. `/media/` will be created in `OUTPUT_PATH`.
|
||||
|
||||
`-p` won't work as manim would look for video player in the container system, which it does not have.
|
||||
|
||||
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.
|
||||
|
||||
@@ -93,49 +108,26 @@ python3 -m manim example_scenes.py SquareToCircle -pl
|
||||
The `-p` flag in the command above is for previewing, meaning the video file will automatically open when it is done rendering. The `-l` flag is for a faster rendering at a lower quality.
|
||||
|
||||
Some other useful flags include:
|
||||
|
||||
* `-s` to skip to the end and just show the final frame.
|
||||
* `-n <number>` to skip ahead to the `n`'th animation of a scene.
|
||||
* `-f` to show the file in finder (for OSX).
|
||||
|
||||
Set `MEDIA_DIR` environment variable to specify where the image and animation files will be written.
|
||||
|
||||
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 with those old projects. 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.
|
||||
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 with those old projects. 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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### Documentation
|
||||
Documentation is in progress at [manim.readthedocs.io](https://manim.readthedocs.io).
|
||||
Documentation is in progress at [eulertour.com/docs](https://www.eulertour.com/docs/).
|
||||
|
||||
### 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).
|
||||
|
||||
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.
|
||||
|
||||
## Contributing
|
||||
Is always welcome. In particular, there is a dire need for tests and documentation.
|
||||
|
||||
|
||||
## License
|
||||
All files in the directories active_projects and old_projects, which by and large generate the visuals for 3b1b videos, are copyright 3Blue1Brown.
|
||||
All files in the directory `from_3b1b`, 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.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
from active_projects.ode.part3.staging import *
|
||||
from active_projects.ode.part3.temperature_graphs import *
|
||||
|
||||
|
||||
OUTPUT_DIRECTORY = "ode/part3"
|
||||
SCENES_IN_ORDER = [
|
||||
FourierSeriesIllustraiton,
|
||||
FourierNameIntro,
|
||||
CircleAnimationOfF,
|
||||
LastChapterWrapper,
|
||||
ThreeMainObservations,
|
||||
SimpleSinExpGraph,
|
||||
]
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part2.fourier_series import FourierOfName
|
||||
|
||||
name_color_pairs = [
|
||||
|
||||
]
|
||||
|
||||
circle_counts = [
|
||||
# 10,
|
||||
# 25,
|
||||
100,
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
for name, color in name_color_pairs:
|
||||
for n_circles in circle_counts:
|
||||
try:
|
||||
first_name = name.split(" ")[0]
|
||||
scene = FourierOfName(
|
||||
name_text=name,
|
||||
name_color=color,
|
||||
n_circles=n_circles,
|
||||
file_writer_config={
|
||||
"write_to_movie": True,
|
||||
"output_directory": os.path.join(
|
||||
"patron_fourier_names",
|
||||
first_name,
|
||||
),
|
||||
"file_name": "{}_Fouierified_{}_Separate_paths".format(
|
||||
first_name,
|
||||
n_circles
|
||||
),
|
||||
},
|
||||
camera_config={
|
||||
"frame_rate": 24,
|
||||
},
|
||||
)
|
||||
except:
|
||||
pass
|
||||
@@ -1,427 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
from active_projects.ode.part2.fourier_series import FourierOfTrebleClef
|
||||
|
||||
|
||||
class FourierNameIntro(Scene):
|
||||
def construct(self):
|
||||
self.show_two_titles()
|
||||
self.transition_to_image()
|
||||
self.show_paper()
|
||||
|
||||
def show_two_titles(self):
|
||||
lt = TextMobject("Fourier", "Series")
|
||||
rt = TextMobject("Fourier", "Transform")
|
||||
lt_variants = VGroup(
|
||||
TextMobject("Complex", "Fourier Series"),
|
||||
TextMobject("Discrete", "Fourier Series"),
|
||||
)
|
||||
rt_variants = VGroup(
|
||||
TextMobject("Discrete", "Fourier Transform"),
|
||||
TextMobject("Fast", "Fourier Transform"),
|
||||
TextMobject("Quantum", "Fourier Transform"),
|
||||
)
|
||||
|
||||
titles = VGroup(lt, rt)
|
||||
titles.scale(1.5)
|
||||
for title, vect in (lt, LEFT), (rt, RIGHT):
|
||||
title.move_to(vect * FRAME_WIDTH / 4)
|
||||
title.to_edge(UP)
|
||||
|
||||
for title, variants in (lt, lt_variants), (rt, rt_variants):
|
||||
title.save_state()
|
||||
title.target = title.copy()
|
||||
title.target.scale(1 / 1.5, about_edge=RIGHT)
|
||||
for variant in variants:
|
||||
variant.move_to(title.target, UR)
|
||||
variant[0].set_color(YELLOW)
|
||||
|
||||
v_line = Line(UP, DOWN)
|
||||
v_line.set_height(FRAME_HEIGHT)
|
||||
v_line.set_stroke(WHITE, 2)
|
||||
|
||||
self.play(
|
||||
FadeInFrom(lt, RIGHT),
|
||||
ShowCreation(v_line)
|
||||
)
|
||||
self.play(
|
||||
FadeInFrom(rt, LEFT),
|
||||
)
|
||||
# Edit in images of circle animations
|
||||
# and clips from FT video
|
||||
|
||||
# for title, variants in (rt, rt_variants), (lt, lt_variants):
|
||||
for title, variants in [(rt, rt_variants)]:
|
||||
# Maybe do it for left variant, maybe not...
|
||||
self.play(
|
||||
MoveToTarget(title),
|
||||
FadeInFrom(variants[0][0], LEFT)
|
||||
)
|
||||
for v1, v2 in zip(variants, variants[1:]):
|
||||
self.play(
|
||||
FadeOutAndShift(v1[0], UP),
|
||||
FadeInFrom(v2[0], DOWN),
|
||||
run_time=0.5,
|
||||
)
|
||||
self.wait(0.5)
|
||||
self.play(
|
||||
Restore(title),
|
||||
FadeOut(variants[-1][0])
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.titles = titles
|
||||
self.v_line = v_line
|
||||
|
||||
def transition_to_image(self):
|
||||
titles = self.titles
|
||||
v_line = self.v_line
|
||||
|
||||
image = ImageMobject("Joseph Fourier")
|
||||
image.set_height(5)
|
||||
image.to_edge(LEFT)
|
||||
|
||||
frame = Rectangle()
|
||||
frame.replace(image, stretch=True)
|
||||
|
||||
name = TextMobject("Joseph", "Fourier")
|
||||
fourier_part = name.get_part_by_tex("Fourier")
|
||||
fourier_part.set_color(YELLOW)
|
||||
F_sym = fourier_part[0]
|
||||
name.match_width(image)
|
||||
name.next_to(image, DOWN)
|
||||
|
||||
self.play(
|
||||
ReplacementTransform(v_line, frame),
|
||||
FadeIn(image),
|
||||
FadeIn(name[0]),
|
||||
*[
|
||||
ReplacementTransform(
|
||||
title[0].deepcopy(),
|
||||
name[1]
|
||||
)
|
||||
for title in titles
|
||||
],
|
||||
titles.scale, 0.65,
|
||||
titles.arrange, DOWN,
|
||||
titles.next_to, image, UP,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
big_F = F_sym.copy()
|
||||
big_F.set_fill(opacity=0)
|
||||
big_F.set_stroke(WHITE, 2)
|
||||
big_F.set_height(3)
|
||||
big_F.move_to(midpoint(
|
||||
image.get_right(),
|
||||
RIGHT_SIDE,
|
||||
))
|
||||
big_F.shift(DOWN)
|
||||
equivalence = VGroup(
|
||||
fourier_part.copy().scale(1.25),
|
||||
TexMobject("\\Leftrightarrow").scale(1.5),
|
||||
TextMobject("Break down into\\\\pure frequencies"),
|
||||
)
|
||||
equivalence.arrange(RIGHT)
|
||||
equivalence.move_to(big_F)
|
||||
equivalence.to_edge(UP)
|
||||
|
||||
self.play(
|
||||
FadeIn(big_F),
|
||||
TransformFromCopy(fourier_part, equivalence[0]),
|
||||
Write(equivalence[1:]),
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(FadeOut(VGroup(big_F, equivalence)))
|
||||
|
||||
self.image = image
|
||||
self.name = name
|
||||
|
||||
def show_paper(self):
|
||||
image = self.image
|
||||
paper = ImageMobject("Fourier paper")
|
||||
paper.match_height(image)
|
||||
paper.next_to(image, RIGHT, MED_LARGE_BUFF)
|
||||
|
||||
date = TexMobject("1822")
|
||||
date.next_to(paper, DOWN)
|
||||
date_rect = SurroundingRectangle(date)
|
||||
date_rect.scale(0.3)
|
||||
date_rect.set_color(RED)
|
||||
date_rect.shift(1.37 * UP + 0.08 * LEFT)
|
||||
date_arrow = Arrow(
|
||||
date_rect.get_bottom(),
|
||||
date.get_top(),
|
||||
buff=SMALL_BUFF,
|
||||
color=date_rect.get_color(),
|
||||
)
|
||||
|
||||
heat_rect = SurroundingRectangle(
|
||||
TextMobject("CHALEUR")
|
||||
)
|
||||
heat_rect.set_color(RED)
|
||||
heat_rect.scale(0.6)
|
||||
heat_rect.move_to(
|
||||
paper.get_top() +
|
||||
1.22 * DOWN + 0.37 * RIGHT
|
||||
)
|
||||
heat_word = TextMobject("Heat")
|
||||
heat_word.scale(1.5)
|
||||
heat_word.next_to(paper, UP)
|
||||
heat_word.shift(paper.get_width() * RIGHT)
|
||||
heat_arrow = Arrow(
|
||||
heat_rect.get_top(),
|
||||
heat_word.get_left(),
|
||||
buff=0.1,
|
||||
path_arc=-60 * DEGREES,
|
||||
color=heat_rect.get_color(),
|
||||
)
|
||||
|
||||
self.play(FadeInFrom(paper, LEFT))
|
||||
self.play(
|
||||
ShowCreation(date_rect),
|
||||
)
|
||||
self.play(
|
||||
GrowFromPoint(date, date_arrow.get_start()),
|
||||
ShowCreation(date_arrow),
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
# Insert animation of circles/sine waves
|
||||
# approximating a square wave
|
||||
|
||||
self.play(
|
||||
ShowCreation(heat_rect),
|
||||
)
|
||||
self.play(
|
||||
GrowFromPoint(heat_word, heat_arrow.get_start()),
|
||||
ShowCreation(heat_arrow),
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class FourierSeriesIllustraiton(Scene):
|
||||
CONFIG = {
|
||||
"n_range": range(1, 31, 2),
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
n_range = self.n_range
|
||||
|
||||
axes1 = Axes(
|
||||
number_line_config={
|
||||
"include_tip": False,
|
||||
},
|
||||
x_axis_config={
|
||||
"tick_frequency": 1 / 4,
|
||||
"unit_size": 4,
|
||||
},
|
||||
x_min=0,
|
||||
x_max=1,
|
||||
y_min=-1,
|
||||
y_max=1,
|
||||
)
|
||||
axes2 = axes1.copy()
|
||||
step_func = axes2.get_graph(
|
||||
lambda x: (1 if x < 0.5 else -1),
|
||||
discontinuities=[0.5],
|
||||
color=YELLOW,
|
||||
stroke_width=3,
|
||||
)
|
||||
dot = Dot(axes2.c2p(0.5, 0), color=step_func.get_color())
|
||||
dot.scale(0.5)
|
||||
step_func.add(dot)
|
||||
axes2.add(step_func)
|
||||
|
||||
arrow = Arrow(LEFT, RIGHT, color=WHITE)
|
||||
VGroup(axes1, arrow, axes2).arrange(RIGHT).shift(UP)
|
||||
|
||||
def generate_nth_func(n):
|
||||
return lambda x: (4 / n / PI) * np.sin(TAU * n * x)
|
||||
|
||||
def generate_kth_partial_sum_func(k):
|
||||
return lambda x: np.sum([
|
||||
generate_nth_func(n)(x)
|
||||
for n in n_range[:k]
|
||||
])
|
||||
|
||||
sine_graphs = VGroup(*[
|
||||
axes1.get_graph(generate_nth_func(n))
|
||||
for n in n_range
|
||||
])
|
||||
sine_graphs.set_stroke(width=3)
|
||||
sine_graphs.set_color_by_gradient(
|
||||
BLUE, GREEN, RED, YELLOW, PINK,
|
||||
BLUE, GREEN, RED, YELLOW, PINK,
|
||||
)
|
||||
|
||||
partial_sums = VGroup(*[
|
||||
axes1.get_graph(generate_kth_partial_sum_func(k + 1))
|
||||
for k in range(len(n_range))
|
||||
])
|
||||
partial_sums.match_style(sine_graphs)
|
||||
|
||||
sum_tex = TexMobject(
|
||||
"\\frac{4}{\\pi}"
|
||||
"\\sum_{1, 3, 5, \\dots}"
|
||||
"\\frac{1}{n} \\sin(2\\pi \\cdot n \\cdot x)"
|
||||
)
|
||||
sum_tex.next_to(partial_sums, DOWN, buff=0.7)
|
||||
eq = TexMobject("=")
|
||||
step_tex = TexMobject(
|
||||
"""
|
||||
1 \\quad \\text{if $x < 0.5$} \\\\
|
||||
0 \\quad \\text{if $x = 0.5$} \\\\
|
||||
-1 \\quad \\text{if $x > 0.5$} \\\\
|
||||
"""
|
||||
)
|
||||
lb = Brace(step_tex, LEFT, buff=SMALL_BUFF)
|
||||
step_tex.add(lb)
|
||||
step_tex.next_to(axes2, DOWN, buff=MED_LARGE_BUFF)
|
||||
eq.move_to(midpoint(
|
||||
step_tex.get_left(),
|
||||
sum_tex.get_right()
|
||||
))
|
||||
|
||||
rects = it.chain(
|
||||
[
|
||||
SurroundingRectangle(sum_tex[0][i])
|
||||
for i in [4, 6, 8]
|
||||
],
|
||||
it.cycle([None])
|
||||
)
|
||||
|
||||
self.add(axes1, arrow, axes2)
|
||||
self.add(step_func)
|
||||
self.add(sum_tex, eq, step_tex)
|
||||
|
||||
curr_partial_sum = axes1.get_graph(lambda x: 0)
|
||||
curr_partial_sum.set_stroke(width=1)
|
||||
for sine_graph, partial_sum, rect in zip(sine_graphs, partial_sums, rects):
|
||||
anims1 = [
|
||||
ShowCreation(sine_graph)
|
||||
]
|
||||
partial_sum.set_stroke(BLACK, 4, background=True)
|
||||
anims2 = [
|
||||
curr_partial_sum.set_stroke,
|
||||
{"width": 1, "opacity": 0.5},
|
||||
curr_partial_sum.set_stroke,
|
||||
{"width": 0, "background": True},
|
||||
ReplacementTransform(
|
||||
sine_graph, partial_sum,
|
||||
remover=True
|
||||
),
|
||||
]
|
||||
if rect:
|
||||
rect.match_style(sine_graph)
|
||||
anims1.append(ShowCreation(rect))
|
||||
anims2.append(FadeOut(rect))
|
||||
self.play(*anims1)
|
||||
self.play(*anims2)
|
||||
curr_partial_sum = partial_sum
|
||||
|
||||
|
||||
class CircleAnimationOfF(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 3,
|
||||
"n_circles": 200,
|
||||
"run_time": 10,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
path = VMobject()
|
||||
shape = TexMobject("F")
|
||||
for sp in shape.family_members_with_points():
|
||||
path.append_points(sp.points)
|
||||
return path
|
||||
|
||||
|
||||
class LastChapterWrapper(Scene):
|
||||
def construct(self):
|
||||
full_rect = FullScreenFadeRectangle(
|
||||
fill_color=DARK_GREY,
|
||||
fill_opacity=1,
|
||||
)
|
||||
rect = ScreenRectangle(height=6)
|
||||
rect.set_stroke(WHITE, 2)
|
||||
rect.set_fill(BLACK, 1)
|
||||
title = TextMobject("Last chapter")
|
||||
title.scale(2)
|
||||
title.to_edge(UP)
|
||||
rect.next_to(title, DOWN)
|
||||
|
||||
self.add(full_rect)
|
||||
self.play(
|
||||
FadeIn(rect),
|
||||
Write(title, run_time=2),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ThreeMainObservations(Scene):
|
||||
def construct(self):
|
||||
fourier = ImageMobject("Joseph Fourier")
|
||||
fourier.set_height(5)
|
||||
fourier.to_corner(DR)
|
||||
fourier.shift(LEFT)
|
||||
bubble = ThoughtBubble(
|
||||
direction=RIGHT,
|
||||
height=3,
|
||||
width=4,
|
||||
)
|
||||
bubble.move_tip_to(fourier.get_corner(UL) + 0.5 * DR)
|
||||
|
||||
observations = VGroup(
|
||||
TextMobject(
|
||||
"1)",
|
||||
# "Sine waves",
|
||||
# "H",
|
||||
# "Heat equation",
|
||||
),
|
||||
TextMobject(
|
||||
"2)",
|
||||
# "Linearity"
|
||||
),
|
||||
TextMobject(
|
||||
"3)",
|
||||
# "Any$^{*}$ function is\\\\",
|
||||
# "a sum of sine waves",
|
||||
),
|
||||
)
|
||||
# heart = SuitSymbol("hearts")
|
||||
# heart.replace(observations[0][2])
|
||||
# observations[0][2].become(heart)
|
||||
# observations[0][1].add(happiness)
|
||||
# observations[2][2].align_to(
|
||||
# observations[2][1], LEFT,
|
||||
# )
|
||||
|
||||
observations.arrange(
|
||||
DOWN,
|
||||
aligned_edge=LEFT,
|
||||
buff=LARGE_BUFF,
|
||||
)
|
||||
observations.set_height(FRAME_HEIGHT - 2)
|
||||
observations.to_corner(UL, buff=LARGE_BUFF)
|
||||
|
||||
self.add(fourier)
|
||||
self.play(ShowCreation(bubble))
|
||||
self.wait()
|
||||
self.play(LaggedStart(*[
|
||||
TransformFromCopy(bubble, observation)
|
||||
for observation in observations
|
||||
], lag_ratio=0.2))
|
||||
self.play(
|
||||
FadeOut(fourier),
|
||||
FadeOut(bubble),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class NewSceneName(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
@@ -1,261 +0,0 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class TemperatureGraphScene(SpecialThreeDScene):
|
||||
CONFIG = {
|
||||
"axes_config": {
|
||||
"x_min": 0,
|
||||
"x_max": TAU,
|
||||
"y_min": 0,
|
||||
"y_max": 10,
|
||||
"z_min": -3,
|
||||
"z_max": 3,
|
||||
"x_axis_config": {
|
||||
"tick_frequency": TAU / 8,
|
||||
"include_tip": False,
|
||||
},
|
||||
"num_axis_pieces": 1,
|
||||
},
|
||||
"default_graph_style": {
|
||||
"stroke_width": 2,
|
||||
"stroke_color": WHITE,
|
||||
"background_image_file": "VerticalTempGradient",
|
||||
},
|
||||
"default_surface_style": {
|
||||
"fill_opacity": 0.1,
|
||||
"checkerboard_colors": [LIGHT_GREY],
|
||||
"stroke_width": 0.5,
|
||||
"stroke_color": WHITE,
|
||||
"stroke_opacity": 0.5,
|
||||
},
|
||||
}
|
||||
|
||||
def get_three_d_axes(self, include_labels=True):
|
||||
axes = ThreeDAxes(**self.axes_config)
|
||||
axes.set_stroke(width=2)
|
||||
|
||||
# Add number labels
|
||||
# TODO?
|
||||
|
||||
# Add axis labels
|
||||
if include_labels:
|
||||
x_label = TexMobject("x")
|
||||
x_label.next_to(axes.x_axis.get_right(), DOWN)
|
||||
axes.x_axis.add(x_label)
|
||||
|
||||
t_label = TextMobject("Time")
|
||||
t_label.rotate(90 * DEGREES, OUT)
|
||||
t_label.next_to(axes.y_axis.get_top(), DL)
|
||||
axes.y_axis.add(t_label)
|
||||
|
||||
temp_label = TextMobject("Temperature")
|
||||
temp_label.rotate(90 * DEGREES, RIGHT)
|
||||
temp_label.next_to(axes.z_axis.get_zenith(), RIGHT)
|
||||
axes.z_axis.add(temp_label)
|
||||
|
||||
# Adjust axis orinetations
|
||||
axes.x_axis.rotate(
|
||||
90 * DEGREES, RIGHT,
|
||||
about_point=axes.c2p(0, 0, 0),
|
||||
)
|
||||
axes.y_axis.rotate(
|
||||
90 * DEGREES, UP,
|
||||
about_point=axes.c2p(0, 0, 0),
|
||||
)
|
||||
|
||||
# Add xy-plane
|
||||
surface_config = {
|
||||
"u_min": 0,
|
||||
"u_max": axes.x_max,
|
||||
"v_min": 0,
|
||||
"v_max": axes.y_max,
|
||||
"resolution": (16, 10),
|
||||
}
|
||||
axes.surface_config = surface_config
|
||||
input_plane = ParametricSurface(
|
||||
lambda x, t: axes.c2p(x, t, 0),
|
||||
# lambda x, t: np.array([x, t, 0]),
|
||||
**surface_config,
|
||||
)
|
||||
input_plane.set_style(
|
||||
fill_opacity=0.5,
|
||||
fill_color=BLUE_B,
|
||||
stroke_width=0.5,
|
||||
stroke_color=WHITE,
|
||||
)
|
||||
|
||||
axes.input_plane = input_plane
|
||||
|
||||
return axes
|
||||
|
||||
def get_initial_state_graph(self, axes, func, **kwargs):
|
||||
config = dict()
|
||||
config.update(self.default_graph_style)
|
||||
config.update(kwargs)
|
||||
return ParametricFunction(
|
||||
lambda x: axes.c2p(
|
||||
x, 0, func(x)
|
||||
),
|
||||
t_min=axes.x_min,
|
||||
t_max=axes.x_max,
|
||||
**config,
|
||||
)
|
||||
|
||||
def get_surface(self, axes, func, **kwargs):
|
||||
config = dict()
|
||||
config.update(axes.surface_config)
|
||||
config.update(self.default_surface_style)
|
||||
config.update(kwargs)
|
||||
return ParametricSurface(
|
||||
lambda x, t: axes.c2p(
|
||||
x, t, func(x, t)
|
||||
),
|
||||
**config
|
||||
)
|
||||
|
||||
def orient_three_d_mobject(self, mobject,
|
||||
phi=85 * DEGREES,
|
||||
theta=-80 * DEGREES):
|
||||
mobject.rotate(-90 * DEGREES - theta, OUT)
|
||||
mobject.rotate(phi, LEFT)
|
||||
return mobject
|
||||
|
||||
|
||||
class SimpleSinExpGraph(TemperatureGraphScene):
|
||||
def construct(self):
|
||||
axes = self.get_three_d_axes()
|
||||
sine_graph = self.get_sine_graph(axes)
|
||||
sine_exp_surface = self.get_sine_exp_surface(axes)
|
||||
|
||||
self.set_camera_orientation(
|
||||
phi=80 * DEGREES,
|
||||
theta=-80 * DEGREES,
|
||||
)
|
||||
self.camera.frame_center.shift(3 * RIGHT)
|
||||
self.begin_ambient_camera_rotation(rate=0.01)
|
||||
|
||||
self.add(axes)
|
||||
self.play(ShowCreation(sine_graph))
|
||||
self.play(UpdateFromAlphaFunc(
|
||||
sine_exp_surface,
|
||||
lambda m, a: m.become(
|
||||
self.get_sine_exp_surface(axes, v_max=a * 10)
|
||||
),
|
||||
run_time=3
|
||||
))
|
||||
self.wait(20)
|
||||
|
||||
#
|
||||
def sin_exp(self, x, t, A=2, omega=1, k=0.25):
|
||||
return A * np.sin(omega * x) * np.exp(-k * (omega**2) * t)
|
||||
|
||||
def get_sine_graph(self, axes, **config):
|
||||
return self.get_initial_state_graph(
|
||||
axes,
|
||||
lambda x: self.sin_exp(x, 0),
|
||||
**config
|
||||
)
|
||||
|
||||
def get_sine_exp_surface(self, axes, **config):
|
||||
return self.get_surface(
|
||||
axes,
|
||||
lambda x, t: self.sin_exp(x, t),
|
||||
**config
|
||||
)
|
||||
|
||||
|
||||
class AddMultipleSolutions(SimpleSinExpGraph):
|
||||
CONFIG = {
|
||||
"axes_config": {
|
||||
"x_axis_config": {
|
||||
"unit_size": 0.7,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
axes1, axes2, axes3 = all_axes = VGroup(*[
|
||||
self.get_three_d_axes(
|
||||
include_labels=False,
|
||||
)
|
||||
for x in range(3)
|
||||
])
|
||||
all_axes.scale(0.5)
|
||||
self.orient_three_d_mobject(all_axes)
|
||||
|
||||
As = [1.5, 1.5]
|
||||
omegas = [1, 2]
|
||||
ks = [0.25, 0.01]
|
||||
quads = [
|
||||
(axes1, [As[0]], [omegas[0]], [ks[0]]),
|
||||
(axes2, [As[1]], [omegas[1]], [ks[1]]),
|
||||
(axes3, As, omegas, ks),
|
||||
]
|
||||
|
||||
for axes, As, omegas, ks in quads:
|
||||
graph = self.get_initial_state_graph(
|
||||
axes,
|
||||
lambda x: np.sum([
|
||||
self.sin_exp(x, 0, A, omega, k)
|
||||
for A, omega, k in zip(As, omegas, ks)
|
||||
])
|
||||
)
|
||||
surface = self.get_surface(
|
||||
axes,
|
||||
lambda x, t: np.sum([
|
||||
self.sin_exp(x, t, A, omega)
|
||||
for A, omega in zip(As, omegas)
|
||||
])
|
||||
)
|
||||
surface.sort(lambda p: -p[2])
|
||||
|
||||
axes.add(surface, graph)
|
||||
axes.graph = graph
|
||||
axes.surface = surface
|
||||
|
||||
self.set_camera_orientation(distance=100)
|
||||
plus = TexMobject("+").scale(2)
|
||||
equals = TexMobject("=").scale(2)
|
||||
group = VGroup(
|
||||
axes1, plus, axes2, equals, axes3,
|
||||
)
|
||||
group.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
|
||||
for axes in all_axes:
|
||||
checkmark = TexMobject("\\checkmark")
|
||||
checkmark.set_color(GREEN)
|
||||
checkmark.scale(2)
|
||||
checkmark.next_to(axes, UP)
|
||||
checkmark.shift(0.7 * DOWN)
|
||||
axes.checkmark = checkmark
|
||||
|
||||
self.add(axes1, axes2)
|
||||
self.play(
|
||||
LaggedStart(
|
||||
Write(axes1.surface),
|
||||
Write(axes2.surface),
|
||||
),
|
||||
LaggedStart(
|
||||
FadeInFrom(axes1.checkmark, DOWN),
|
||||
FadeInFrom(axes2.checkmark, DOWN),
|
||||
),
|
||||
lag_ratio=0.2,
|
||||
run_time=1,
|
||||
)
|
||||
self.wait()
|
||||
self.play(Write(plus))
|
||||
self.play(
|
||||
Transform(
|
||||
axes1.copy().set_fill(opacity=0),
|
||||
axes3
|
||||
),
|
||||
Transform(
|
||||
axes2.copy().set_fill(opacity=0),
|
||||
axes3
|
||||
),
|
||||
FadeInFrom(equals, LEFT)
|
||||
)
|
||||
self.play(
|
||||
FadeInFrom(axes3.checkmark, DOWN),
|
||||
)
|
||||
self.wait()
|
||||
@@ -6,13 +6,11 @@ services:
|
||||
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
|
||||
- --media_dir=/tmp/output
|
||||
volumes:
|
||||
- ${INPUT_PATH:?INPUT_PATH environment variable isn't set}:/tmp/input
|
||||
- ${OUTPUT_PATH:?OUTPUT_PATH environment variable isn't set}:/tmp/output
|
||||
working_dir: /tmp/input
|
||||
network_mode: "none"
|
||||
|
||||
210
docs/source/animation.rst
Normal file
210
docs/source/animation.rst
Normal file
@@ -0,0 +1,210 @@
|
||||
Animation
|
||||
=========
|
||||
|
||||
|
||||
|
||||
The simplest of which is ``Scene.add``. The object appears on the first frame
|
||||
without any animation::
|
||||
|
||||
class NoAnimation(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
self.add(square))
|
||||
|
||||
Animation are used in conjunction with ``scene.Play``
|
||||
|
||||
Fade
|
||||
----
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationFadeIn.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationFadeIn(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
|
||||
anno = TextMobject("Fade In")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
self.play(FadeIn(square))
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationFadeOut.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationFadeOut(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
|
||||
anno = TextMobject("Fade Out")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
self.add(square)
|
||||
self.play(FadeOut(square))
|
||||
|
||||
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationFadeInFrom.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationFadeInFrom(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
for label, edge in zip(
|
||||
["LEFT", "RIGHT", "UP", "DOWN"], [LEFT, RIGHT, UP, DOWN]
|
||||
):
|
||||
anno = TextMobject(f"Fade In from {label}")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
|
||||
self.play(FadeInFrom(square, edge))
|
||||
self.remove(anno, square)
|
||||
|
||||
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationFadeOutAndShift.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationFadeOutAndShift(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
for label, edge in zip(
|
||||
["LEFT", "RIGHT", "UP", "DOWN"], [LEFT, RIGHT, UP, DOWN]
|
||||
):
|
||||
anno = TextMobject(f"Fade Out and shift {label}")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
|
||||
self.play(FadeOutAndShift(square, edge))
|
||||
self.remove(anno, square)
|
||||
|
||||
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationFadeInFromLarge.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationFadeInFromLarge(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
|
||||
for factor in [0.1, 0.5, 0.8, 1, 2, 5]:
|
||||
anno = TextMobject(f"Fade In from large scale\_factor={factor}")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
|
||||
self.play(FadeInFromLarge(square, scale_factor=factor))
|
||||
self.remove(anno, square)
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationFadeInFromPoint.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationFadeInFromPoint(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
for i in range(-6, 7, 2):
|
||||
anno = TextMobject(f"Fade In from point {i}")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
self.play(FadeInFromPoint(square, point=i))
|
||||
self.remove(anno, square)
|
||||
|
||||
|
||||
|
||||
Grow
|
||||
----
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationGrowFromEdge.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationGrowFromEdge(Scene):
|
||||
def construct(self):
|
||||
|
||||
for label, edge in zip(
|
||||
["LEFT", "RIGHT", "UP", "DOWN"], [LEFT, RIGHT, UP, DOWN]
|
||||
):
|
||||
anno = TextMobject(f"Grow from {label} edge")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
square = Square()
|
||||
self.play(GrowFromEdge(square, edge))
|
||||
self.remove(anno, square)
|
||||
|
||||
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationGrowFromCenter.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationGrowFromCenter(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
|
||||
anno = TextMobject("Grow from center")
|
||||
anno.shift(2 * DOWN)
|
||||
self.add(anno)
|
||||
|
||||
self.play(GrowFromCenter(square))
|
||||
|
||||
|
||||
|
||||
|
||||
Diagonal Directions
|
||||
-------------------
|
||||
|
||||
You can combine cardinal directions to form diagonal animations
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="560" height="315" controls>
|
||||
<source src="_static/AnimationFadeInFromDiagonal.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class AnimationFadeInFromDiagonal(Scene):
|
||||
def construct(self):
|
||||
square = Square()
|
||||
for diag in [UP + LEFT, UP + RIGHT, DOWN + LEFT, DOWN + RIGHT]:
|
||||
self.play(FadeInFrom(square, diag))
|
||||
|
||||
.. note::
|
||||
You can also use the abbreviated forms like ``UL, UR, DL, DR``.
|
||||
See :ref:`ref-directions`.
|
||||
BIN
docs/source/assets/AnimationFadeIn.mp4
Normal file
BIN
docs/source/assets/AnimationFadeIn.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationFadeInFrom.mp4
Normal file
BIN
docs/source/assets/AnimationFadeInFrom.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationFadeInFromDiagonal.mp4
Normal file
BIN
docs/source/assets/AnimationFadeInFromDiagonal.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationFadeInFromLarge.mp4
Normal file
BIN
docs/source/assets/AnimationFadeInFromLarge.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationFadeInFromPoint.mp4
Normal file
BIN
docs/source/assets/AnimationFadeInFromPoint.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationFadeInFromSmall.mp4
Normal file
BIN
docs/source/assets/AnimationFadeInFromSmall.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationFadeOut.mp4
Normal file
BIN
docs/source/assets/AnimationFadeOut.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationFadeOutAndShift.mp4
Normal file
BIN
docs/source/assets/AnimationFadeOutAndShift.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationGrowFromCenter.mp4
Normal file
BIN
docs/source/assets/AnimationGrowFromCenter.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/AnimationGrowFromEdge.mp4
Normal file
BIN
docs/source/assets/AnimationGrowFromEdge.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/SquareToCircle.mp4
Normal file
BIN
docs/source/assets/SquareToCircle.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/coordinate/CoorAlias.mp4
Normal file
BIN
docs/source/assets/coordinate/CoorAlias.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/coordinate/CoorArithmetic.mp4
Normal file
BIN
docs/source/assets/coordinate/CoorArithmetic.mp4
Normal file
Binary file not shown.
BIN
docs/source/assets/coordinate/CoorPolygon.png
Normal file
BIN
docs/source/assets/coordinate/CoorPolygon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/source/assets/coordinate/DotMap.mp4
Normal file
BIN
docs/source/assets/coordinate/DotMap.mp4
Normal file
Binary file not shown.
@@ -23,6 +23,7 @@ author = 'EulerTour'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
master_doc = 'index'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
@@ -49,4 +50,4 @@ html_theme = 'sphinx_rtd_theme'
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ['assets']
|
||||
|
||||
94
docs/source/constants.rst
Normal file
94
docs/source/constants.rst
Normal file
@@ -0,0 +1,94 @@
|
||||
Manim Constants
|
||||
===============
|
||||
|
||||
The ``constants.py`` under ``manimlib/`` contains variables that are used
|
||||
during setup and running manim. Some variables are not documented here as they are
|
||||
only used internally by manim.
|
||||
|
||||
Directories
|
||||
-----------
|
||||
|
||||
MEDIA_DIR
|
||||
The directory where ``VIDEO_DIR`` and ``TEX_DIR`` will be created,
|
||||
if they aren't specified via flags.
|
||||
VIDEO_DIR
|
||||
Used to store the scenes rendered by Manim. When a scene is
|
||||
finished rendering, it will be stored under
|
||||
``VIDEO_DIR/module_name/scene_name/quality/scene_name.mp4``.
|
||||
Created under ``MEDIA_DIR`` by default.
|
||||
TEX_DIR
|
||||
Files written by Latex are stored here. It also acts as a cache
|
||||
so that the files aren't rewritten each time Latex is needed.
|
||||
|
||||
Those directories are created if they don't exist.
|
||||
|
||||
Tex
|
||||
---
|
||||
TEX_USE_CTEX
|
||||
A boolean value. Change it to True if you need to use Chinese typesetting.
|
||||
TEX_TEXT_TO_REPLACE
|
||||
Placeholder text used by manim when generating tex files
|
||||
TEMPLATE_TEX_FILE
|
||||
By default ``manimlib/tex_template.tex`` is used. If ``TEX_USE_CTEX``
|
||||
is set to True then ``manimlib/ctex_template.tex`` is used.
|
||||
|
||||
Numerical Constants
|
||||
-------------------
|
||||
|
||||
PI
|
||||
alias to ``numpy.pi``
|
||||
TAU
|
||||
PI * 2
|
||||
|
||||
DEGREES
|
||||
TAU / 360
|
||||
|
||||
Camera Configuration
|
||||
--------------------
|
||||
|
||||
Render setting presets
|
||||
|
||||
PRODUCTION_QUALITY_CAMERA_CONFIG
|
||||
2560x1440 @ 60fps # This is the default when rendering a scene
|
||||
HIGH_QUALITY_CAMERA_CONFIG
|
||||
1920x1080 @ 60fps. # Used when the ``-h`` or ``--high_quality`` flag
|
||||
is passed.
|
||||
MEDIUM_QUALITY_CAMERA_CONFIG
|
||||
1280x720 @ 30fps. # Used when the ``-m`` or ``--medium_quality``
|
||||
flag is passed.
|
||||
LOW_QUALITY_CAMERA_CONFIG
|
||||
854x480 @ 15fps. # Used when the ``-l`` or ``--low_quality`` flag is
|
||||
passed.
|
||||
|
||||
.. _ref-directions:
|
||||
|
||||
Coordinates
|
||||
-----------
|
||||
|
||||
Used for 2d/3d animations and placements::
|
||||
|
||||
ORIGIN
|
||||
UP
|
||||
DOWN
|
||||
RIGHT
|
||||
LEFT
|
||||
IN # 3d camera only, away from camera
|
||||
OUT # 3d camera only, close to camera
|
||||
|
||||
UL = UP + LEFT # diagonal abbreviations. You can use either one
|
||||
UR = UP + RIGHT
|
||||
DL = DOWN + LEFT
|
||||
DR = DOWN + RIGHT
|
||||
|
||||
TOP
|
||||
BOTTOM
|
||||
LEFT_SIDE
|
||||
RIGHT_SIDE``
|
||||
|
||||
Colors
|
||||
------
|
||||
|
||||
COLOR_MAP
|
||||
A predefined color maps
|
||||
PALETTE
|
||||
A list of color hex strings, derived from COLOR_MAP
|
||||
178
docs/source/coordinate.rst
Normal file
178
docs/source/coordinate.rst
Normal file
@@ -0,0 +1,178 @@
|
||||
Coordinate
|
||||
==========
|
||||
|
||||
By default, the scene in manim is made up by 8 x 14 grid. The grid is addressed using a numpy
|
||||
array in the form of [x, y, z]. For 2D animations only the x and y axes are used.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class DotMap(Scene):
|
||||
def construct(self):
|
||||
dots = dict()
|
||||
annos = dict()
|
||||
var_index = 0
|
||||
for x in range(-7, 8):
|
||||
for y in range(-4, 5):
|
||||
annos[f"{x}{y}"] = TexMobject(f"({x}, {y})")
|
||||
dots[f"{var_index}"] = Dot(np.array([x, y, 0]))
|
||||
var_index = var_index + 1
|
||||
for anno, dot in zip(annos.values(), dots.values()):
|
||||
self.add(anno)
|
||||
self.add(dot)
|
||||
self.wait(0.2)
|
||||
self.remove(anno)
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="700" height="394" controls>
|
||||
<source src="_static/coordinate/DotMap.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. note::
|
||||
You can place objects outside this boundary, but it won't show up in the render.
|
||||
|
||||
Using Coordinates
|
||||
-----------------
|
||||
|
||||
Coordinates are used for creating geometries (`VMobject` in manim) and animations.
|
||||
|
||||
Here coordinates are used to create this Polygon
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CoorPolygon(Scene):
|
||||
def construct(self):
|
||||
for x in range(-7, 8):
|
||||
for y in range(-4, 5):
|
||||
self.add(Dot(np.array([x, y, 0]), color=DARK_GREY))
|
||||
polygon = Polygon(
|
||||
np.array([3, 2, 0]),
|
||||
np.array([1, -1, 0]),
|
||||
np.array([-5, -4, 0]),
|
||||
np.array([-4, 4, 0]))
|
||||
self.add(polygon)
|
||||
|
||||
|
||||
.. Image:: assets/coordinate/CoorPolygon.png
|
||||
:width: 700px
|
||||
|
||||
Coordinate Aliasing
|
||||
-------------------
|
||||
|
||||
From some animations typing a ``np.array`` everytime you need a coordinate can be tedious.
|
||||
Manim provides aliases to the most common coordinates::
|
||||
|
||||
UP == np.array([0, 1, 0])
|
||||
DOWN == np.array([0, -1, 0])
|
||||
LEFT == np.array([-1, 0, 0])
|
||||
RIGHT == np.array([1, 0, 0])
|
||||
UL == np.array([-1, 1, 0])
|
||||
DL == np.array([-1, -1, 0])
|
||||
UR == np.array([1, 1, 0])
|
||||
DR == np.array([1, -1, 0])
|
||||
|
||||
Here coordinates are used for animations
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CoorAlias(Scene):
|
||||
def construct(self):
|
||||
for x in range(-7, 8):
|
||||
for y in range(-4, 5):
|
||||
self.add(Dot(np.array([x, y, 0]), color=DARK_GREY))
|
||||
|
||||
aliases = {
|
||||
"UP": UP,
|
||||
"np.array([0,1,0])": np.array([0, 1, 0]),
|
||||
"DOWN": DOWN,
|
||||
"np.array([0,-1,0])": np.array([0, -1, 0]),
|
||||
"LEFT": LEFT,
|
||||
"np.array([-1,0,0])": np.array([-1, 0, 0]),
|
||||
"RIGHT": RIGHT,
|
||||
"np.array([1,0,0])": np.array([1, 0, 0]),
|
||||
"UL": UL,
|
||||
"np.array([-1,1,0])": np.array([-1, 1, 0]),
|
||||
"DL": DL,
|
||||
"np.array([-1,-1,0])": np.array([-1, -1, 0]),
|
||||
"UR": UR,
|
||||
"np.array([1,1,0])": np.array([1, 1, 0]),
|
||||
"DR": DR,
|
||||
"np.array([1,-1,0])": np.array([1, -1, 0])}
|
||||
circle = Circle(color=RED, radius=0.5)
|
||||
self.add(circle)
|
||||
self.wait(0.5)
|
||||
|
||||
for text, aliase in aliases.items():
|
||||
anno = TexMobject(f"\\texttt{{{text}}}")
|
||||
self.play(Write(anno, run_time=0.2))
|
||||
self.play(ApplyMethod(circle.shift, aliase))
|
||||
self.wait(0.2)
|
||||
self.play(FadeOut(anno, run_time=0.2))
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="700" height="394" controls>
|
||||
<source src="_static/coordinate/CoorAlias.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
Coordinate Arithmetic
|
||||
---------------------
|
||||
|
||||
Numpy array allows arithmetic operations::
|
||||
|
||||
>>> numpy.array([2,2,0]) + 4
|
||||
array([6, 6, 4])
|
||||
|
||||
>>> np.array([1, -3, 0]) + np.array([-4, 2, 0])
|
||||
array([-3, -1, 0])
|
||||
|
||||
>>> np.array([2, 2, 0]) - np.array([3,6, 0])
|
||||
array([-1, -4, 0])
|
||||
|
||||
>>> numpy.array([2,2,0]) - 3
|
||||
array([-1, -1, -3])
|
||||
|
||||
>>> np.array([1, -3, 0]) * 3
|
||||
array([ 3, -9, 0])
|
||||
|
||||
>>> numpy.array([2,2,0]) / 2
|
||||
array([1., 1., 0.])
|
||||
|
||||
>>> numpy.array([2,2,0]) / numpy.array([1, 4, 0])
|
||||
__main__:1: RuntimeWarning: invalid value encountered in true_divide
|
||||
array([2. , 0.5, nan])
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class CoorArithmetic(Scene):
|
||||
def construct(self):
|
||||
for x in range(-7, 8):
|
||||
for y in range(-4, 5):
|
||||
self.add(Dot(np.array([x, y, 0]), color=DARK_GREY))
|
||||
|
||||
circle = Circle(color=RED, radius=0.5)
|
||||
self.add(circle)
|
||||
self.wait(0.5)
|
||||
|
||||
aliases = {
|
||||
"LEFT * 3": LEFT * 3,
|
||||
"UP + RIGHT / 2": UP + RIGHT / 2,
|
||||
"DOWN + LEFT * 2": DOWN + LEFT * 2,
|
||||
"RIGHT * 3.75 * DOWN": RIGHT * 3.75 * DOWN,
|
||||
# certain arithmetic won't work as you expected
|
||||
# In [4]: RIGHT * 3.75 * DOWN
|
||||
# Out[4]: array([ 0., -0., 0.])
|
||||
"RIGHT * 3.75 + DOWN": RIGHT * 3.75 + DOWN}
|
||||
|
||||
for text, aliase in aliases.items():
|
||||
anno = TexMobject(f"\\texttt{{{text}}}")
|
||||
self.play(Write(anno, run_time=0.2))
|
||||
self.play(ApplyMethod(circle.shift, aliase))
|
||||
self.wait(0.2)
|
||||
self.play(FadeOut(anno, run_time=0.2))
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video width="700" height="394" controls>
|
||||
<source src="_static/coordinate/CoorArithmetic.mp4" type="video/mp4">
|
||||
</video>
|
||||
@@ -2,14 +2,14 @@ Getting Started
|
||||
===============
|
||||
|
||||
Todd Zimmerman put together `a very nice tutorial`_ on getting started with
|
||||
``manim``, but is unfortunately outdated. It's still useful for understanding
|
||||
how ``manim`` is used, but the examples won't run on the latest version of
|
||||
``manim``.
|
||||
``manim``, which has been updated to run on python 3.7. Note that you'll want
|
||||
to change `from big_ol_pile_of_manim_imports import *` to `from
|
||||
manimlib.imports import *` to work with the current codebase.
|
||||
|
||||
.. _a very nice tutorial: https://talkingphysics.wordpress.com/2018/06/11/learning-how-to-animate-videos-using-``manim``-series-a-journey/
|
||||
.. _a very nice tutorial: https://talkingphysics.wordpress.com/2019/01/08/getting-started-animating-with-manim-and-python-3-7/
|
||||
|
||||
.. toctree::
|
||||
:caption: Contents:
|
||||
:caption: Contents
|
||||
:maxdepth: 2
|
||||
|
||||
learning_by_example
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
Learning by Example
|
||||
===================
|
||||
|
||||
You create videos in manim by writing :class:`~scene.scene.Scene` instances.
|
||||
``example_scenes.py`` contains a few simple ones that we can use to learn about
|
||||
manim. For instance, take ``SquareToCircle``.
|
||||
SquareToCircle
|
||||
--------------
|
||||
|
||||
``example_scenes.py`` contains simple examples that we can use to learn about manim.
|
||||
|
||||
Go ahead and try out the ``SquareToCircle`` scene by running it with ``$ manim example_scenes.py SquareToCircle -p``
|
||||
in manim directory.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from manimlib.imports import *
|
||||
|
||||
class SquareToCircle(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
@@ -20,113 +26,106 @@ manim. For instance, take ``SquareToCircle``.
|
||||
self.play(Transform(square, circle))
|
||||
self.play(FadeOut(square))
|
||||
|
||||
:meth:`~scene.scene.Scene.construct` specifies what is displayed on the screen
|
||||
when the :class:`~scene.scene.Scene` is rendered to video. You can render a
|
||||
:class:`~scene.scene.Scene` by running ``extract_scene.py``. Run ``python
|
||||
extract_scene.py -h`` to see how it's used.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
> python extract_scene.py -h
|
||||
usage: extract_scene.py [-h] [-p] [-w] [-s] [-l] [-m] [-g] [-f] [-t] [-q] [-a]
|
||||
[-o OUTPUT_NAME] [-n START_AT_ANIMATION_NUMBER]
|
||||
[-r RESOLUTION] [-c COLOR] [-d OUTPUT_DIRECTORY]
|
||||
file [scene_name]
|
||||
|
||||
positional arguments:
|
||||
file path to file holding the python code for the scene
|
||||
scene_name Name of the Scene class you want to see
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-p, --preview
|
||||
-w, --write_to_movie
|
||||
-s, --show_last_frame
|
||||
-l, --low_quality
|
||||
-m, --medium_quality
|
||||
-g, --save_pngs
|
||||
-f, --show_file_in_finder
|
||||
-t, --transparent
|
||||
-q, --quiet
|
||||
-a, --write_all
|
||||
-o OUTPUT_NAME, --output_name OUTPUT_NAME
|
||||
-n START_AT_ANIMATION_NUMBER, --start_at_animation_number START_AT_ANIMATION_NUMBER
|
||||
-r RESOLUTION, --resolution RESOLUTION
|
||||
-c COLOR, --color COLOR
|
||||
-d OUTPUT_DIRECTORY, --output_directory OUTPUT_DIRECTORY
|
||||
|
||||
The most common flags are ``-p``, to automatically play the generated video,
|
||||
``-l``, to render in lower quality in favor of speed, and ``-s``, to show the
|
||||
last frame of the :class:`~scene.scene.Scene` for faster development. Run
|
||||
``python extract_scene.py example_scenes.py SquareToCircle -pl`` to produce a
|
||||
file called SquareToCircle.mp4 in the media directory that you have configured,
|
||||
and automatically play it.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/8tvYDIGLJJA?ecver=1" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
|
||||
<video width="560" height="315" controls>
|
||||
<source src="../_static/SquareToCircle.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
Let's step through each line of the :class:`~scene.scene.Scene`. Lines 3 and 4
|
||||
instantiate a :class:`~mobject.geometry.Circle` and
|
||||
:class:`~mobject.geometry.Square`, respectively. Both of these subclass
|
||||
:class:`~mobject.mobject.Mobject`, the base class for objects in manim. Note
|
||||
|
||||
.. note::
|
||||
|
||||
The flag ``-p`` plays the rendered video with default video player.
|
||||
|
||||
Other frequently used flags are:
|
||||
|
||||
* ``-l`` for rendering video in lower resolution (which renders faster)
|
||||
* ``-s`` to show the last frame of the video.
|
||||
|
||||
Run ``manim -h`` all the available flags (``python -m manim -h`` if you installed it to a venv)
|
||||
|
||||
|
||||
Let's step through each line of ``SquareToCircle``
|
||||
|
||||
.. code-block:: python
|
||||
:lineno-start: 3
|
||||
|
||||
class SquareToCircle(Scene):
|
||||
|
||||
You create videos in manim by writing :class:`~scene.scene.Scene` classes.
|
||||
|
||||
Each :class:`~scene.scene.Scene` in manim is self-contained. That means everything
|
||||
you created under this scene does not exist outside the class.
|
||||
|
||||
.. code-block:: python
|
||||
:lineno-start: 4
|
||||
|
||||
def construct(self):
|
||||
|
||||
:meth:`~scene.scene.Scene.construct` specifies what is displayed on the screen
|
||||
when the :class:`~scene.scene.Scene` is rendered to video.
|
||||
|
||||
.. code-block:: python
|
||||
:lineno-start: 5
|
||||
|
||||
circle = Circle()
|
||||
square = Square()
|
||||
|
||||
``Circle()`` and ``Square()`` create :class:`~mobject.geometry.Circle` and :class:`~mobject.geometry.Square`.
|
||||
|
||||
Both of these are instances of :class:`~mobject.mobject.Mobject` subclasses, the base class for objects in manim. Note
|
||||
that instantiating a :class:`~mobject.mobject.Mobject` does not add it to the
|
||||
:class:`~scene.scene.Scene`, so you wouldn't see anything if you were to render
|
||||
the :class:`~scene.scene.Scene` at this point.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
:lineno-start: 3
|
||||
|
||||
circle = Circle()
|
||||
square = Square()
|
||||
|
||||
Lines 5, 6, and 7 apply various modifications to the mobjects before animating
|
||||
them. The call to :meth:`~mobject.mobject.Mobject.flip` on line 5 flips the
|
||||
:class:`~mobject.geometry.Square` across the RIGHT vector. This is equivalent
|
||||
to a refection across the x-axis. Then the call to
|
||||
:meth:`~mobject.mobject.Mobject.rotate` on line 6 rotates the
|
||||
:class:`~mobject.geometry.Square` 3/8ths of a full rotation counterclockwise.
|
||||
Finally, the call to :meth:`~mobject.mobject.Mobject.set_fill` on line 7 sets
|
||||
the fill color for the :class:`~mobject.geometry.Circle` to pink, and its
|
||||
opacity to 0.5.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
:lineno-start: 5
|
||||
:lineno-start: 7
|
||||
|
||||
square.flip(RIGHT)
|
||||
square.rotate(-3 * TAU / 8)
|
||||
circle.set_fill(PINK, opacity=0.5)
|
||||
|
||||
Line 9 is the first to generate video.
|
||||
:class:`~animation.creation.ShowCreation`,
|
||||
:class:`~animation.transform.Transform`, and
|
||||
:class:`~animation.creation.FadeOut` are
|
||||
:class:`~animation.animation.Animation` instances. Each
|
||||
:class:`~animation.animation.Animation` takes one or more
|
||||
:class:`~mobject.mobject.Mobject` instances as arguments, which it animates
|
||||
when passed to :meth:`~scene.scene.Scene.play`. This is how video is typically
|
||||
created in manim. :class:`~mobject.mobject.Mobject` instances are automatically
|
||||
added to the :class:`~scene.scene.Scene` when they are animated. You can add a
|
||||
:class:`~mobject.mobject.Mobject` to the :class:`~scene.scene.Scene` manually
|
||||
by passing it as an argument to :meth:`~scene.scene.Scene.add`.
|
||||
``flip()`` ``rotate()`` ``set_fill()`` apply various modifications to the mobjects before animating
|
||||
them. The call to :meth:`~mobject.mobject.Mobject.flip` flips the
|
||||
:class:`~mobject.geometry.Square` across the RIGHT vector. This is equivalent
|
||||
to a refection across the x-axis.
|
||||
|
||||
The call to :meth:`~mobject.mobject.Mobject.rotate` rotates the
|
||||
:class:`~mobject.geometry.Square` 3/8ths of a full rotation counterclockwise.
|
||||
|
||||
The call to :meth:`~mobject.mobject.Mobject.set_fill` sets
|
||||
the fill color for the :class:`~mobject.geometry.Circle` to pink, and its opacity to 0.5.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
:lineno-start: 9
|
||||
:lineno-start: 11
|
||||
|
||||
self.play(ShowCreation(square))
|
||||
self.play(Transform(square, circle))
|
||||
self.play(FadeOut(square))
|
||||
|
||||
:class:`~animation.creation.ShowCreation` draws a
|
||||
:class:`~mobject.mobject.Mobject` to the screen,
|
||||
:class:`~animation.transform.Transform` morphs one
|
||||
:class:`~mobject.mobject.Mobject` into another, and
|
||||
:class:`~animation.creation.FadeOut` fades a
|
||||
:class:`~mobject.mobject.Mobject` out of the :class:`~scene.scene.Scene`. Note
|
||||
that only the first argument to :class:`~animation.transform.Transform` is
|
||||
modified, and the second is not added to the :class:`~scene.scene.Scene`. After
|
||||
line 10 is executed ``square`` is a :class:`~mobject.geometry.Square` instance
|
||||
with the shape of a :class:`~mobject.geometry.Circle`.
|
||||
To generated animation, :class:`~animation.animation.Animation` classes are used.
|
||||
|
||||
Each :class:`~animation.animation.Animation` takes one or more :class:`~mobject.mobject.Mobject` instances as arguments, which it animates
|
||||
when passed to :meth:`~scene.scene.Scene.play`. This is how video is typically
|
||||
created in manim.
|
||||
|
||||
:class:`~mobject.mobject.Mobject` instances are automatically
|
||||
added to the :class:`~scene.scene.Scene` when they are animated. You can add a
|
||||
:class:`~mobject.mobject.Mobject` to the :class:`~scene.scene.Scene` manually
|
||||
by passing it as an argument to :meth:`~scene.scene.Scene.add`.
|
||||
|
||||
|
||||
:class:`~animation.creation.ShowCreation` draws a :class:`~mobject.mobject.Mobject` to the screen.
|
||||
|
||||
:class:`~animation.transform.Transform` morphs one :class:`~mobject.mobject.Mobject` into another.
|
||||
|
||||
:class:`~animation.creation.FadeOut` fades a :class:`~mobject.mobject.Mobject` out of the :class:`~scene.scene.Scene`.
|
||||
|
||||
.. note::
|
||||
|
||||
Only the first argument to :class:`~animation.transform.Transform` is modified,
|
||||
the second is not added to the :class:`~scene.scene.Scene`. :class:`~animation.tranform.Transform`
|
||||
only changes the appearance but not the underlying properties.
|
||||
|
||||
After the call to ``transform()`` ``square`` is still a :class:`~mobject.geometry.Square` instance
|
||||
but with the shape of :class:`~mobject.geometry.Circle`.
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
Making a Scene
|
||||
==============
|
||||
|
||||
Talk about Scenes and organization, bring it all together.
|
||||
A scene is what renders when manim is executed. Each scene contains mobjects, which can then be animated as
|
||||
previously explained. In code, a scene is a class that extends ``Scene`` and implements the ``construct``
|
||||
function, like so. Manim will execute this function to render the scene.
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
|
||||
from manimlib.imports import *
|
||||
|
||||
class ExampleScene(Scene):
|
||||
def construct(self):
|
||||
# Add and animate mobjects here
|
||||
@@ -6,13 +6,20 @@
|
||||
Welcome to Manim's documentation!
|
||||
=================================
|
||||
|
||||
These docs are generated from the master branch of the
|
||||
`Manim repo <https://github.com/3b1b/manim>`_. You can contribute by submitting
|
||||
a pull request there.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
:caption: Contents
|
||||
|
||||
about
|
||||
installation/index
|
||||
getting_started/index
|
||||
coordinate
|
||||
animation
|
||||
constants
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
||||
@@ -5,7 +5,7 @@ Instructions on installing Manim
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
:caption: Contents
|
||||
|
||||
linux
|
||||
mac
|
||||
|
||||
@@ -39,3 +39,26 @@ the ``activate`` binary by doing ``source bin/activate``, to exit use the ``deac
|
||||
|
||||
texlive texlive-latex-extra texlive-fonts-extra
|
||||
texlive-latex-recommended texlive-science texlive-fonts-extra tipa
|
||||
|
||||
Arch Linux
|
||||
----------
|
||||
Install system libraries::
|
||||
|
||||
# pacman -S cairo ffmpeg opencv sox
|
||||
|
||||
Install Latex distribution::
|
||||
|
||||
# pacman -S texlive-most
|
||||
|
||||
OR install python-manimlib_:sup:`AUR` package::
|
||||
|
||||
$ git clone https://aur.archlinux.org/python-manimlib.git
|
||||
$ cd python-manimlib
|
||||
$ makepkg -si
|
||||
|
||||
You can use AUR helpers such as yay_:sup:`AUR`::
|
||||
|
||||
$ yay -S python-manimlib
|
||||
|
||||
.. _python-manimlib: https://aur.archlinux.org/packages/python-manimlib/
|
||||
.. _yay: https://aur.archlinux.org/packages/yay/
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
Mac
|
||||
===
|
||||
|
||||
A stub for mac installation
|
||||
The simplest way to install the system dependencies on Mac OS X is with Homebrew.
|
||||
Mac come preinstalled with python2, but to use manim, python3 is required
|
||||
|
||||
1. Install python3 https://docs.python.org/3/using/mac.html
|
||||
2. Install Cairo: ``brew install cairo``
|
||||
3. Install Sox: ``brew install sox``
|
||||
4. Install ffmpeg: ``brew install ffmpeg``
|
||||
5. Install latex (MacTeX): ``brew cask install mactex``
|
||||
6. Install manimlib ``pip install manimlib`` (or ``pip install --user manimlib`` to just yourself)
|
||||
|
||||
@@ -1,4 +1,60 @@
|
||||
Windows
|
||||
=======
|
||||
|
||||
A stub for windows installation
|
||||
Install System Libraries
|
||||
------------------------
|
||||
|
||||
Make sure you have *Python 3* for Windows installed first:
|
||||
|
||||
https://www.python.org/downloads/windows/
|
||||
|
||||
Install ffmpeg:
|
||||
|
||||
https://ffmpeg.org/download.html#build-windows
|
||||
|
||||
Install sox:
|
||||
|
||||
http://sox.sourceforge.net/Main/HomePage
|
||||
|
||||
Install a latex distribution. On Windows MikTex is commonly used:
|
||||
|
||||
https://miktex.org/howto/install-miktex
|
||||
|
||||
Path configuration
|
||||
------------------
|
||||
|
||||
To invoke commandline without supplying path to the binary
|
||||
the PATH environment needs to be configured. Below are template examples, please change
|
||||
the path according to your username and specific python version. Assuming all the
|
||||
softwares are installed with no alteration to the installation paths::
|
||||
|
||||
C:\Users\$username\AppData\local\Programs\Python\Python$version\
|
||||
C:\Users\$username\AppData\local\Programs\Python\Python$version\Scripts\
|
||||
C:\MikTex\miktex\bin\x64\
|
||||
C:\ffmpeg\bin\
|
||||
|
||||
The path entries should be separated by semicolon.
|
||||
|
||||
Installing python packages and manim
|
||||
------------------------------------
|
||||
|
||||
Make sure you can start pip using ``pip`` in your commandline. Then do
|
||||
``pip install pyreadline`` for the ``readline`` package.
|
||||
|
||||
Grab the pycairo wheel binary ``pycairo‑1.18.0‑cp37‑cp37m‑win32.whl`` from https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo
|
||||
and install it via ``python -m pip install C:\absolute\path\to\the\whl\file``
|
||||
|
||||
clone the manim repository if you have git ``git clone https://github.com/3b1b/manim`` or download the zip file from
|
||||
the repository page with ``Clone or download`` button and unzip it.
|
||||
|
||||
Open the commandline within the manim directory with ``Shift + Right click`` on an empty space in the folder and select ``open command window here``
|
||||
|
||||
Install manim python dependencies with ``pip install -r requirements.txt``
|
||||
|
||||
Test the installation
|
||||
---------------------
|
||||
|
||||
Type in ``python -m manim -h`` and if nothing went wrong during the installation process you should see the help text.
|
||||
|
||||
Use ``python -m manim example_scenes.py SquareToCircle -pl`` to render the example scene and the file should play after rendering. The movie file should be
|
||||
in ``media/videos/example_scenes/480p15``
|
||||
|
||||
@@ -5,7 +5,7 @@ channels:
|
||||
dependencies:
|
||||
- python=3.7
|
||||
- cairo
|
||||
- ffmpeg
|
||||
- ffmpeg
|
||||
- colour==0.1.5
|
||||
- numpy==1.15.0
|
||||
- pillow==5.2.0
|
||||
@@ -14,4 +14,6 @@ dependencies:
|
||||
- opencv==3.4.2
|
||||
- pycairo==1.18.0
|
||||
- pydub==0.23.0
|
||||
- pyreadline
|
||||
- ffmpeg
|
||||
- pip:
|
||||
- pyreadline
|
||||
|
||||
1152
from_3b1b/active/bayes/footnote.py
Normal file
1152
from_3b1b/active/bayes/footnote.py
Normal file
File diff suppressed because it is too large
Load Diff
4830
from_3b1b/active/bayes/part1.py
Normal file
4830
from_3b1b/active/bayes/part1.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
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 *
|
||||
from active_projects.diffyq.part1.pendulum import *
|
||||
from active_projects.diffyq.part1.staging import *
|
||||
from active_projects.diffyq.part1.pi_scenes import *
|
||||
from active_projects.diffyq.part1.phase_space import *
|
||||
from active_projects.diffyq.part1.wordy_scenes import *
|
||||
|
||||
OUTPUT_DIRECTORY = "ode/part1"
|
||||
OUTPUT_DIRECTORY = "diffyq/part1"
|
||||
SCENES_IN_ORDER = [
|
||||
WhenChangeIsEasier,
|
||||
VectorFieldTest,
|
||||
@@ -1,10 +1,10 @@
|
||||
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 *
|
||||
from active_projects.diffyq.part2.staging import *
|
||||
from active_projects.diffyq.part2.fourier_series import *
|
||||
from active_projects.diffyq.part2.heat_equation import *
|
||||
from active_projects.diffyq.part2.pi_scenes import *
|
||||
from active_projects.diffyq.part2.wordy_scenes import *
|
||||
|
||||
OUTPUT_DIRECTORY = "ode/part2"
|
||||
OUTPUT_DIRECTORY = "diffyq/part2"
|
||||
SCENES_IN_ORDER = [
|
||||
PartTwoOfTour,
|
||||
HeatEquationIntroTitle,
|
||||
70
from_3b1b/active/diffyq/all_part3_scenes.py
Normal file
70
from_3b1b/active/diffyq/all_part3_scenes.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from active_projects.diffyq.part3.staging import *
|
||||
from active_projects.diffyq.part3.temperature_graphs import *
|
||||
from active_projects.diffyq.part3.pi_creature_scenes import *
|
||||
from active_projects.diffyq.part3.wordy_scenes import *
|
||||
from active_projects.diffyq.part3.discrete_case import *
|
||||
|
||||
|
||||
OUTPUT_DIRECTORY = "diffyq/part3"
|
||||
SCENES_IN_ORDER = [
|
||||
LastChapterWrapper,
|
||||
ThreeConstraints,
|
||||
OceanOfPossibilities,
|
||||
ThreeMainObservations,
|
||||
SimpleCosExpGraph,
|
||||
AddMultipleSolutions,
|
||||
FourierSeriesIllustraiton,
|
||||
BreakDownAFunction,
|
||||
SineCurveIsUnrealistic,
|
||||
AnalyzeSineCurve,
|
||||
EquationAboveSineAnalysis,
|
||||
ExponentialDecay,
|
||||
InvestmentGrowth,
|
||||
GrowingPileOfMoney,
|
||||
CarbonDecayCurve,
|
||||
CarbonDecayingInMammoth,
|
||||
SineWaveScaledByExp,
|
||||
ShowSinExpDerivatives,
|
||||
IfOnly,
|
||||
BoundaryConditionInterlude,
|
||||
BoundaryConditionReference,
|
||||
GiantCross,
|
||||
SimulateRealSineCurve,
|
||||
DerivativesOfLinearFunction,
|
||||
StraightLine3DGraph,
|
||||
SimulateLinearGraph,
|
||||
EmphasizeBoundaryPoints,
|
||||
ShowNewRuleAtDiscreteBoundary,
|
||||
DiscreteEvolutionPoint25,
|
||||
DiscreteEvolutionPoint1,
|
||||
FlatEdgesForDiscreteEvolution,
|
||||
FlatEdgesForDiscreteEvolutionTinySteps,
|
||||
FlatEdgesContinuousEvolution,
|
||||
FlatAtBoundaryWords,
|
||||
SlopeToHeatFlow,
|
||||
CloserLookAtStraightLine,
|
||||
WriteOutBoundaryCondition,
|
||||
SoWeGotNowhere,
|
||||
ManipulateSinExpSurface,
|
||||
HeatEquationFrame,
|
||||
ShowFreq1CosExpDecay,
|
||||
ShowFreq2CosExpDecay,
|
||||
ShowFreq4CosExpDecay,
|
||||
CompareFreqDecays1to2,
|
||||
CompareFreqDecays1to4,
|
||||
CompareFreqDecays2to4,
|
||||
ShowHarmonics,
|
||||
ShowHarmonicSurfaces,
|
||||
|
||||
# SimpleCosExpGraph,
|
||||
# AddMultipleSolutions,
|
||||
# IveHeardOfThis,
|
||||
# FourierSeriesOfLineIllustration,
|
||||
# InFouriersShoes,
|
||||
]
|
||||
|
||||
PART_4_SCENES = [
|
||||
FourierSeriesIllustraiton,
|
||||
FourierNameIntro,
|
||||
CircleAnimationOfF,
|
||||
]
|
||||
65
from_3b1b/active/diffyq/all_part4_scenes.py
Normal file
65
from_3b1b/active/diffyq/all_part4_scenes.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from active_projects.diffyq.part4.staging import *
|
||||
from active_projects.diffyq.part4.fourier_series_scenes import *
|
||||
from active_projects.diffyq.part4.pi_creature_scenes import *
|
||||
from active_projects.diffyq.part4.three_d_graphs import *
|
||||
from active_projects.diffyq.part4.temperature_scenes import *
|
||||
from active_projects.diffyq.part4.complex_functions import *
|
||||
from active_projects.diffyq.part4.long_fourier_scenes import *
|
||||
|
||||
from active_projects.diffyq.part3.staging import *
|
||||
|
||||
OUTPUT_DIRECTORY = "diffyq/part4"
|
||||
SCENES_IN_ORDER = [
|
||||
ComplexFourierSeriesExample,
|
||||
FourierOfFourier,
|
||||
FourierOfFourierZoomedIn,
|
||||
FourierOfFourier100xZoom,
|
||||
FourierSeriesFormula,
|
||||
RelationToOtherVideos,
|
||||
WhyWouldYouCare,
|
||||
ShowLinearity,
|
||||
CombineSeveralSolutions,
|
||||
FourierGainsImmortality,
|
||||
SolveForWavesNothingElse,
|
||||
CycleThroughManyLinearCombinations,
|
||||
StepFunctionExample,
|
||||
WhichWavesAreAvailable,
|
||||
AlternateBoundaryConditions,
|
||||
AskQuestionOfGraph,
|
||||
CommentOnFouriersImmortality,
|
||||
HangOnThere,
|
||||
ShowInfiniteSum,
|
||||
TechnicalNuances,
|
||||
BreakDownStepFunction,
|
||||
StepFunctionSolutionFormla,
|
||||
# How to compute
|
||||
FourierSeriesOfLineIllustration,
|
||||
GeneralizeToComplexFunctions,
|
||||
ClarifyInputAndOutput,
|
||||
GraphForFlattenedPi,
|
||||
PiFourierSeries,
|
||||
RealValuedFunctionFourierSeries,
|
||||
YouSaidThisWasEasier,
|
||||
AskAboutComplexNotVector,
|
||||
SimpleComplexExponentExample,
|
||||
LooseWithLanguage,
|
||||
DemonstrateAddingArrows,
|
||||
TRangingFrom0To1,
|
||||
LabelRotatingVectors,
|
||||
IntegralTrick,
|
||||
SwapIntegralAndSum,
|
||||
FootnoteOnSwappingIntegralAndSum,
|
||||
FormulaOutOfContext,
|
||||
ShowRangeOfCnFormulas,
|
||||
DescribeSVG,
|
||||
# TODO
|
||||
IncreaseOrderOfApproximation,
|
||||
ShowStepFunctionIn2dView,
|
||||
StepFunctionIntegral,
|
||||
GeneralChallenge,
|
||||
|
||||
# Oldies
|
||||
# FourierSeriesIllustraiton,
|
||||
# FourierNameIntro,
|
||||
# CircleAnimationOfF,
|
||||
]
|
||||
5
from_3b1b/active/diffyq/all_part5_scenes.py
Normal file
5
from_3b1b/active/diffyq/all_part5_scenes.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from active_projects.diffyq.part5.staging import *
|
||||
|
||||
OUTPUT_DIRECTORY = "diffyq/part5"
|
||||
SCENES_IN_ORDER = [
|
||||
]
|
||||
23
from_3b1b/active/diffyq/fourier_montage_scenes.py
Normal file
23
from_3b1b/active/diffyq/fourier_montage_scenes.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from active_projects.diffyq.part4.long_fourier_scenes import *
|
||||
|
||||
OUTPUT_DIRECTORY = "diffyq/part4"
|
||||
SCENES_IN_ORDER = [
|
||||
ZoomedInFourierSeriesExample,
|
||||
FourierSeriesExampleWithRectForZoom,
|
||||
ZoomedInFourierSeriesExample100x,
|
||||
FourierOfFourier100xZoom,
|
||||
FourierOfFourierZoomedIn,
|
||||
FourierOfFourier,
|
||||
SigmaZoomedInFourierSeriesExample,
|
||||
SigmaFourierSeriesExampleWithRectForZoom,
|
||||
NailAndGearZoomedInFourierSeriesExample,
|
||||
NailAndGearFourierSeriesExampleWithRectForZoom,
|
||||
TrebleClefZoomedInFourierSeriesExample,
|
||||
TrebleClefFourierSeriesExampleWithRectForZoom,
|
||||
FourierOfSeattle,
|
||||
FourierOfSeattleZoomedIn,
|
||||
FourierOfBritain,
|
||||
FourierOfBritainZoomedIn,
|
||||
FourierOfHilbert,
|
||||
FourierOfHilbertZoomedIn,
|
||||
]
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.shared_constructs import *
|
||||
from active_projects.diffyq.part1.shared_constructs import *
|
||||
|
||||
|
||||
class Pendulum(VGroup):
|
||||
@@ -254,7 +254,7 @@ class ThetaVsTAxes(Axes):
|
||||
"tick_frequency": PI / 8,
|
||||
"unit_size": 1.5,
|
||||
},
|
||||
"number_line_config": {
|
||||
"axis_config": {
|
||||
"color": "#EEEEEE",
|
||||
"stroke_width": 2,
|
||||
"include_tip": False,
|
||||
@@ -371,7 +371,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||
"tip_length": 0.3,
|
||||
},
|
||||
"x_max": 12,
|
||||
"number_line_config": {
|
||||
"axis_config": {
|
||||
"stroke_width": 2,
|
||||
}
|
||||
},
|
||||
@@ -750,7 +750,7 @@ class LowAnglePendulum(Scene):
|
||||
"number_scale_val": 0.5,
|
||||
},
|
||||
"x_max": 25,
|
||||
"number_line_config": {
|
||||
"axis_config": {
|
||||
"tip_length": 0.3,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
@@ -837,7 +837,7 @@ class MediumAnglePendulum(LowAnglePendulum):
|
||||
"y_axis_config": {"unit_size": 0.75},
|
||||
"y_max": PI / 2,
|
||||
"y_min": -PI / 2,
|
||||
"number_line_config": {
|
||||
"axis_config": {
|
||||
"tip_length": 0.3,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
@@ -867,7 +867,7 @@ class HighAnglePendulum(LowAnglePendulum):
|
||||
"y_axis_config": {"unit_size": 0.5},
|
||||
"y_max": PI,
|
||||
"y_min": -PI,
|
||||
"number_line_config": {
|
||||
"axis_config": {
|
||||
"tip_length": 0.3,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
@@ -888,7 +888,7 @@ class VeryLowAnglePendulum(LowAnglePendulum):
|
||||
"y_axis_config": {"unit_size": 2},
|
||||
"y_max": PI / 4,
|
||||
"y_min": -PI / 4,
|
||||
"number_line_config": {
|
||||
"axis_config": {
|
||||
"tip_length": 0.3,
|
||||
"stroke_width": 2,
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.shared_constructs import *
|
||||
from active_projects.ode.part1.pendulum import Pendulum
|
||||
from active_projects.diffyq.part1.shared_constructs import *
|
||||
from active_projects.diffyq.part1.pendulum import Pendulum
|
||||
|
||||
|
||||
# TODO: Arguably separate the part showing many
|
||||
@@ -2012,11 +2012,18 @@ class ManyStepsFromDifferentStartingPoints(TakeManyTinySteps):
|
||||
class Thumbnail(IntroduceVectorField):
|
||||
CONFIG = {
|
||||
"vector_field_config": {
|
||||
"delta_x": 0.5,
|
||||
"delta_y": 0.5,
|
||||
# "delta_x": 0.5,
|
||||
# "delta_y": 0.5,
|
||||
# "max_magnitude": 5,
|
||||
# "length_func": lambda norm: 0.5 * sigmoid(norm),
|
||||
"delta_x": 1,
|
||||
"delta_y": 1,
|
||||
"max_magnitude": 5,
|
||||
"length_func": lambda norm: 0.5 * sigmoid(norm),
|
||||
}
|
||||
"length_func": lambda norm: 0.9 * sigmoid(norm),
|
||||
},
|
||||
"big_pendulum_config": {
|
||||
"damping": 0.4,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
@@ -2027,7 +2034,7 @@ class Thumbnail(IntroduceVectorField):
|
||||
field = self.vector_field
|
||||
field.set_stroke(width=5)
|
||||
for vector in field:
|
||||
vector.set_stroke(width=3)
|
||||
vector.set_stroke(width=8)
|
||||
vector.tip.set_stroke(width=0)
|
||||
vector.tip.scale(1.5, about_point=vector.get_last_point())
|
||||
vector.set_opacity(1)
|
||||
@@ -2063,17 +2070,43 @@ class Thumbnail(IntroduceVectorField):
|
||||
new_mob.set_stroke(width=0)
|
||||
black_parts.add(new_mob)
|
||||
|
||||
for vect in field:
|
||||
for mob in title.family_members_with_points():
|
||||
for p in [vect.get_start(), vect.get_end()]:
|
||||
x, y = p[:2]
|
||||
x0, y0 = mob.get_corner(DL)[:2]
|
||||
x1, y1 = mob.get_corner(UR)[:2]
|
||||
if x0 < x < x1 and y0 < y < y1:
|
||||
vect.set_opacity(0.25)
|
||||
vect.tip.set_stroke(width=0)
|
||||
# for vect in field:
|
||||
# for mob in title.family_members_with_points():
|
||||
# for p in [vect.get_start(), vect.get_end()]:
|
||||
# x, y = p[:2]
|
||||
# x0, y0 = mob.get_corner(DL)[:2]
|
||||
# x1, y1 = mob.get_corner(UR)[:2]
|
||||
# if x0 < x < x1 and y0 < y < y1:
|
||||
# vect.set_opacity(0.25)
|
||||
# vect.tip.set_stroke(width=0)
|
||||
|
||||
self.add(self.plane)
|
||||
self.add(field)
|
||||
self.add(black_parts)
|
||||
self.add(title)
|
||||
# self.add(black_parts)
|
||||
# self.add(title)
|
||||
|
||||
self.add_line(self.plane)
|
||||
|
||||
def add_line(self, axes):
|
||||
func = self.vector_field_func
|
||||
|
||||
line = VMobject()
|
||||
line.start_new_path(axes.c2p(-TAU, 3.5))
|
||||
|
||||
dt = 0.1
|
||||
t = 0
|
||||
total_time = 40
|
||||
|
||||
while t < total_time:
|
||||
t += dt
|
||||
last_point = line.get_last_point()
|
||||
new_point = last_point + dt * func(last_point)
|
||||
if new_point[0] > FRAME_WIDTH / 2:
|
||||
new_point = last_point + FRAME_WIDTH * LEFT
|
||||
line.start_new_path(new_point)
|
||||
else:
|
||||
line.add_smooth_curve_to(new_point)
|
||||
|
||||
line.set_stroke(WHITE, 6)
|
||||
line.make_smooth()
|
||||
self.add(line)
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.shared_constructs import *
|
||||
from active_projects.diffyq.part1.shared_constructs import *
|
||||
|
||||
|
||||
class SomeOfYouWatching(TeacherStudentsScene):
|
||||
@@ -1,10 +1,10 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.shared_constructs import *
|
||||
from active_projects.ode.part1.pendulum import Pendulum
|
||||
from active_projects.ode.part1.pendulum import ThetaVsTAxes
|
||||
from active_projects.ode.part1.phase_space import IntroduceVectorField
|
||||
from old_projects.div_curl import PhaseSpaceOfPopulationModel
|
||||
from old_projects.div_curl import ShowTwoPopulations
|
||||
from active_projects.diffyq.part1.shared_constructs import *
|
||||
from active_projects.diffyq.part1.pendulum import Pendulum
|
||||
from active_projects.diffyq.part1.pendulum import ThetaVsTAxes
|
||||
from active_projects.diffyq.part1.phase_space import IntroduceVectorField
|
||||
from from_3b1b.old.div_curl import PhaseSpaceOfPopulationModel
|
||||
from from_3b1b.old.div_curl import ShowTwoPopulations
|
||||
|
||||
|
||||
# Scenes
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.shared_constructs import *
|
||||
from active_projects.diffyq.part1.shared_constructs import *
|
||||
|
||||
|
||||
class SmallAngleApproximationTex(Scene):
|
||||
@@ -4,7 +4,7 @@ from manimlib.imports import *
|
||||
|
||||
class FourierCirclesScene(Scene):
|
||||
CONFIG = {
|
||||
"n_circles": 10,
|
||||
"n_vectors": 10,
|
||||
"big_radius": 2,
|
||||
"colors": [
|
||||
BLUE_D,
|
||||
@@ -15,44 +15,57 @@ class FourierCirclesScene(Scene):
|
||||
"circle_style": {
|
||||
"stroke_width": 2,
|
||||
},
|
||||
"arrow_config": {
|
||||
"vector_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,
|
||||
"circle_config": {
|
||||
"stroke_width": 1,
|
||||
},
|
||||
"base_frequency": 1,
|
||||
"slow_factor": 0.25,
|
||||
"center_point": ORIGIN,
|
||||
"parametric_function_step_size": 0.001,
|
||||
"drawn_path_color": YELLOW,
|
||||
"drawn_path_stroke_width": 2,
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
self.slow_factor_tracker = ValueTracker(
|
||||
self.slow_factor
|
||||
)
|
||||
self.vector_clock = ValueTracker(0)
|
||||
self.vector_clock.add_updater(
|
||||
lambda m, dt: m.increment_value(
|
||||
self.get_slow_factor() * dt
|
||||
)
|
||||
)
|
||||
self.add(self.vector_clock)
|
||||
|
||||
def get_slow_factor(self):
|
||||
return self.slow_factor_tracker.get_value()
|
||||
|
||||
def get_vector_time(self):
|
||||
return self.vector_clock.get_value()
|
||||
|
||||
#
|
||||
def get_freqs(self):
|
||||
n = self.n_circles
|
||||
n = self.n_vectors
|
||||
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)]
|
||||
return [complex(0) for x in range(self.n_vectors)]
|
||||
|
||||
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()
|
||||
def get_rotating_vectors(self, freqs=None, coefficients=None):
|
||||
vectors = VGroup()
|
||||
self.center_tracker = VectorizedPoint(self.center_point)
|
||||
|
||||
if freqs is None:
|
||||
@@ -60,80 +73,74 @@ class FourierCirclesScene(Scene):
|
||||
if coefficients is None:
|
||||
coefficients = self.get_coefficients()
|
||||
|
||||
last_circle = None
|
||||
last_vector = None
|
||||
for freq, coefficient in zip(freqs, coefficients):
|
||||
if last_circle:
|
||||
center_func = last_circle.get_start
|
||||
if last_vector:
|
||||
center_func = last_vector.get_end
|
||||
else:
|
||||
center_func = self.center_tracker.get_location
|
||||
circle = self.get_circle(
|
||||
vector = self.get_rotating_vector(
|
||||
coefficient=coefficient,
|
||||
freq=freq,
|
||||
color=next(color_iterator),
|
||||
center_func=center_func,
|
||||
)
|
||||
circles.add(circle)
|
||||
last_circle = circle
|
||||
return circles
|
||||
vectors.add(vector)
|
||||
last_vector = vector
|
||||
return vectors
|
||||
|
||||
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,
|
||||
)
|
||||
def get_rotating_vector(self, coefficient, freq, center_func):
|
||||
vector = Vector(RIGHT, **self.vector_config)
|
||||
vector.scale(abs(coefficient))
|
||||
if abs(coefficient) == 0:
|
||||
phase = 0
|
||||
else:
|
||||
circle.radial_line = Line(
|
||||
*line_points,
|
||||
color=WHITE,
|
||||
**self.circle_style,
|
||||
phase = np.log(coefficient).imag
|
||||
vector.rotate(phase, about_point=ORIGIN)
|
||||
vector.freq = freq
|
||||
vector.coefficient = coefficient
|
||||
vector.center_func = center_func
|
||||
vector.add_updater(self.update_vector)
|
||||
return vector
|
||||
|
||||
def update_vector(self, vector, dt):
|
||||
time = self.get_vector_time()
|
||||
coef = vector.coefficient
|
||||
freq = vector.freq
|
||||
phase = np.log(coef).imag
|
||||
|
||||
vector.set_length(abs(coef))
|
||||
vector.set_angle(phase + time * freq * TAU)
|
||||
vector.shift(vector.center_func() - vector.get_start())
|
||||
return vector
|
||||
|
||||
def get_circles(self, vectors):
|
||||
return VGroup(*[
|
||||
self.get_circle(
|
||||
vector,
|
||||
color=color
|
||||
)
|
||||
circle.add(circle.radial_line)
|
||||
circle.freq = freq
|
||||
circle.phase = phase
|
||||
circle.rotate(phase)
|
||||
circle.coefficient = coefficient
|
||||
circle.center_func = center_func
|
||||
for vector, color in zip(
|
||||
vectors,
|
||||
self.get_color_iterator()
|
||||
)
|
||||
])
|
||||
|
||||
def get_circle(self, vector, color=BLUE):
|
||||
circle = Circle(color=color, **self.circle_config)
|
||||
circle.center_func = vector.get_start
|
||||
circle.radius_func = vector.get_length
|
||||
circle.add_updater(self.update_circle)
|
||||
return circle
|
||||
|
||||
def update_circle(self, circle, dt):
|
||||
circle.rotate(
|
||||
self.get_slow_factor() * circle.freq * dt * TAU
|
||||
)
|
||||
def update_circle(self, circle):
|
||||
circle.set_width(2 * circle.radius_func())
|
||||
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()
|
||||
def get_vector_sum_path(self, vectors, color=YELLOW):
|
||||
coefs = [v.coefficient for v in vectors]
|
||||
freqs = [v.freq for v in vectors]
|
||||
center = vectors[0].get_start()
|
||||
|
||||
path = ParametricFunction(
|
||||
lambda t: center + reduce(op.add, [
|
||||
@@ -150,13 +157,19 @@ class FourierCirclesScene(Scene):
|
||||
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)
|
||||
def get_drawn_path_alpha(self):
|
||||
return self.get_vector_time()
|
||||
|
||||
def get_drawn_path(self, vectors, stroke_width=None, **kwargs):
|
||||
if stroke_width is None:
|
||||
stroke_width = self.drawn_path_stroke_width
|
||||
path = self.get_vector_sum_path(vectors, **kwargs)
|
||||
broken_path = CurvesAsSubmobjects(path)
|
||||
broken_path.curr_time = 0
|
||||
|
||||
def update_path(path, dt):
|
||||
alpha = path.curr_time * self.get_slow_factor()
|
||||
# alpha = path.curr_time * self.get_slow_factor()
|
||||
alpha = self.get_drawn_path_alpha()
|
||||
n_curves = len(path)
|
||||
for a, sp in zip(np.linspace(0, 1, n_curves), path):
|
||||
b = alpha - a
|
||||
@@ -168,17 +181,17 @@ class FourierCirclesScene(Scene):
|
||||
path.curr_time += dt
|
||||
return path
|
||||
|
||||
broken_path.set_color(YELLOW)
|
||||
broken_path.set_color(self.drawn_path_color)
|
||||
broken_path.add_updater(update_path)
|
||||
return broken_path
|
||||
|
||||
def get_y_component_wave(self,
|
||||
circles,
|
||||
vectors,
|
||||
left_x=1,
|
||||
color=PINK,
|
||||
n_copies=2,
|
||||
right_shift_rate=5):
|
||||
path = self.get_circle_end_path(circles)
|
||||
path = self.get_vector_sum_path(vectors)
|
||||
wave = ParametricFunction(
|
||||
lambda t: op.add(
|
||||
right_shift_rate * t * LEFT,
|
||||
@@ -216,15 +229,16 @@ class FourierCirclesScene(Scene):
|
||||
|
||||
return VGroup(wave, wave_copies)
|
||||
|
||||
def get_wave_y_line(self, circles, wave):
|
||||
def get_wave_y_line(self, vectors, wave):
|
||||
return DashedLine(
|
||||
circles[-1].get_start(),
|
||||
vectors[-1].get_end(),
|
||||
wave[0].get_end(),
|
||||
stroke_width=1,
|
||||
dash_length=DEFAULT_DASH_LENGTH * 0.5,
|
||||
)
|
||||
|
||||
# Computing Fourier series
|
||||
# i.e. where all the math happens
|
||||
def get_coefficients_of_path(self, path, n_samples=10000, freqs=None):
|
||||
if freqs is None:
|
||||
freqs = self.get_freqs()
|
||||
@@ -250,7 +264,7 @@ class FourierCirclesScene(Scene):
|
||||
|
||||
class FourierSeriesIntroBackground4(FourierCirclesScene):
|
||||
CONFIG = {
|
||||
"n_circles": 4,
|
||||
"n_vectors": 4,
|
||||
"center_point": 4 * LEFT,
|
||||
"run_time": 30,
|
||||
"big_radius": 1.5,
|
||||
@@ -271,7 +285,7 @@ class FourierSeriesIntroBackground4(FourierCirclesScene):
|
||||
self.wait(self.run_time)
|
||||
|
||||
def get_ks(self):
|
||||
return np.arange(1, 2 * self.n_circles + 1, 2)
|
||||
return np.arange(1, 2 * self.n_vectors + 1, 2)
|
||||
|
||||
def get_freqs(self):
|
||||
return self.base_frequency * self.get_ks()
|
||||
@@ -282,53 +296,70 @@ class FourierSeriesIntroBackground4(FourierCirclesScene):
|
||||
|
||||
class FourierSeriesIntroBackground8(FourierSeriesIntroBackground4):
|
||||
CONFIG = {
|
||||
"n_circles": 8,
|
||||
"n_vectors": 8,
|
||||
}
|
||||
|
||||
|
||||
class FourierSeriesIntroBackground12(FourierSeriesIntroBackground4):
|
||||
CONFIG = {
|
||||
"n_circles": 12,
|
||||
"n_vectors": 12,
|
||||
}
|
||||
|
||||
|
||||
class FourierSeriesIntroBackground20(FourierSeriesIntroBackground4):
|
||||
CONFIG = {
|
||||
"n_circles": 20,
|
||||
"n_vectors": 20,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfPiSymbol(FourierCirclesScene):
|
||||
CONFIG = {
|
||||
"n_circles": 50,
|
||||
"n_vectors": 51,
|
||||
"center_point": ORIGIN,
|
||||
"slow_factor": 0.1,
|
||||
"run_time": 30,
|
||||
"n_cycles": 1,
|
||||
"tex": "\\pi",
|
||||
"start_drawn": False,
|
||||
"max_circle_stroke_width": 1,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_vectors_circles_path()
|
||||
for n in range(self.n_cycles):
|
||||
self.run_one_cycle()
|
||||
|
||||
def add_vectors_circles_path(self):
|
||||
path = self.get_path()
|
||||
coefs = self.get_coefficients_of_path(path)
|
||||
|
||||
circles = self.get_circles(coefficients=coefs)
|
||||
vectors = self.get_rotating_vectors(coefficients=coefs)
|
||||
circles = self.get_circles(vectors)
|
||||
self.set_decreasing_stroke_widths(circles)
|
||||
# approx_path = self.get_circle_end_path(circles)
|
||||
drawn_path = self.get_drawn_path(circles)
|
||||
# approx_path = self.get_vector_sum_path(circles)
|
||||
drawn_path = self.get_drawn_path(vectors)
|
||||
if self.start_drawn:
|
||||
drawn_path.curr_time = 1 / self.slow_factor
|
||||
self.vector_clock.increment_value(1)
|
||||
|
||||
self.add(path)
|
||||
self.add(vectors)
|
||||
self.add(circles)
|
||||
self.add(drawn_path)
|
||||
self.wait(self.run_time)
|
||||
|
||||
self.vectors = vectors
|
||||
self.circles = circles
|
||||
self.path = path
|
||||
self.drawn_path = drawn_path
|
||||
|
||||
def run_one_cycle(self):
|
||||
time = 1 / self.slow_factor
|
||||
self.wait(time)
|
||||
|
||||
def set_decreasing_stroke_widths(self, circles):
|
||||
mcsw = self.max_circle_stroke_width
|
||||
for k, circle in zip(it.count(1), circles):
|
||||
circle.set_stroke(width=max(
|
||||
1 / np.sqrt(k),
|
||||
1,
|
||||
# mcsw / np.sqrt(k),
|
||||
mcsw / k,
|
||||
mcsw,
|
||||
))
|
||||
return circles
|
||||
|
||||
@@ -341,61 +372,87 @@ class FourierOfPiSymbol(FourierCirclesScene):
|
||||
return path
|
||||
|
||||
|
||||
class FourierOfName(FourierOfPiSymbol):
|
||||
class FourierOfTexPaths(FourierOfPiSymbol, MovingCameraScene):
|
||||
CONFIG = {
|
||||
"n_circles": 100,
|
||||
"n_vectors": 100,
|
||||
"name_color": WHITE,
|
||||
"name_text": "Abc",
|
||||
"animated_name": "Abc",
|
||||
"time_per_symbol": 5,
|
||||
"slow_factor": 1 / 5,
|
||||
"parametric_function_step_size": 0.01,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
name = TextMobject(self.name_text)
|
||||
name = TextMobject(self.animated_name)
|
||||
max_width = FRAME_WIDTH - 2
|
||||
max_height = FRAME_HEIGHT - 2
|
||||
name.set_width(max_width)
|
||||
if name.get_height() > max_height:
|
||||
name.set_height(max_height)
|
||||
|
||||
frame = self.camera.frame
|
||||
frame.save_state()
|
||||
|
||||
vectors = VGroup(VectorizedPoint())
|
||||
circles = VGroup(VectorizedPoint())
|
||||
for path in name.family_members_with_points():
|
||||
for subpath in path.get_subpaths():
|
||||
sp_mob = VMobject()
|
||||
sp_mob.set_points(subpath)
|
||||
coefs = self.get_coefficients_of_path(sp_mob)
|
||||
new_circles = self.get_circles(
|
||||
new_vectors = self.get_rotating_vectors(
|
||||
coefficients=coefs
|
||||
)
|
||||
new_circles = self.get_circles(new_vectors)
|
||||
self.set_decreasing_stroke_widths(new_circles)
|
||||
drawn_path = self.get_drawn_path(new_circles)
|
||||
|
||||
drawn_path = self.get_drawn_path(new_vectors)
|
||||
drawn_path.clear_updaters()
|
||||
drawn_path.set_stroke(self.name_color, 3)
|
||||
|
||||
new_circles.suspend_updating()
|
||||
self.play(ReplacementTransform(circles, new_circles))
|
||||
new_circles.resume_updating()
|
||||
circles = new_circles
|
||||
static_vectors = VMobject().become(new_vectors)
|
||||
static_circles = VMobject().become(new_circles)
|
||||
# static_circles = new_circles.deepcopy()
|
||||
# static_vectors.clear_updaters()
|
||||
# static_circles.clear_updaters()
|
||||
|
||||
self.play(
|
||||
Transform(vectors, static_vectors, remover=True),
|
||||
Transform(circles, static_circles, remover=True),
|
||||
frame.set_height, 1.5 * name.get_height(),
|
||||
frame.move_to, path,
|
||||
)
|
||||
|
||||
self.add(new_vectors, new_circles)
|
||||
self.vector_clock.set_value(0)
|
||||
self.play(
|
||||
ShowCreation(drawn_path),
|
||||
rate_func=linear,
|
||||
run_time=self.time_per_symbol
|
||||
)
|
||||
circles.suspend_updating()
|
||||
self.play(FadeOut(circles))
|
||||
self.remove(new_vectors, new_circles)
|
||||
self.add(static_vectors, static_circles)
|
||||
|
||||
vectors = static_vectors
|
||||
circles = static_circles
|
||||
self.play(
|
||||
FadeOut(vectors),
|
||||
Restore(frame),
|
||||
run_time=2
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class FourierOfPiSymbol5(FourierOfPiSymbol):
|
||||
CONFIG = {
|
||||
"n_circles": 5,
|
||||
"n_vectors": 5,
|
||||
"run_time": 10,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfTrebleClef(FourierOfPiSymbol):
|
||||
CONFIG = {
|
||||
"n_circles": 100,
|
||||
"n_vectors": 101,
|
||||
"run_time": 10,
|
||||
"start_drawn": True,
|
||||
"file_name": "TrebleClef",
|
||||
@@ -419,7 +476,7 @@ class FourierOfIP(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"file_name": "IP_logo2",
|
||||
"height": 6,
|
||||
"n_circles": 100,
|
||||
"n_vectors": 100,
|
||||
}
|
||||
|
||||
# def construct(self):
|
||||
@@ -451,7 +508,7 @@ class FourierOfEighthNote(FourierOfTrebleClef):
|
||||
class FourierOfN(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 6,
|
||||
"n_circles": 1000,
|
||||
"n_vectors": 1000,
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
@@ -461,7 +518,7 @@ class FourierOfN(FourierOfTrebleClef):
|
||||
class FourierNailAndGear(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 6,
|
||||
"n_circles": 200,
|
||||
"n_vectors": 200,
|
||||
"run_time": 100,
|
||||
"slow_factor": 0.01,
|
||||
"parametric_function_step_size": 0.0001,
|
||||
@@ -479,7 +536,7 @@ class FourierNailAndGear(FourierOfTrebleClef):
|
||||
class FourierBatman(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 4,
|
||||
"n_circles": 100,
|
||||
"n_vectors": 100,
|
||||
"run_time": 10,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
@@ -495,7 +552,7 @@ class FourierBatman(FourierOfTrebleClef):
|
||||
class FourierHeart(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 4,
|
||||
"n_circles": 100,
|
||||
"n_vectors": 100,
|
||||
"run_time": 10,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
@@ -517,7 +574,7 @@ class FourierHeart(FourierOfTrebleClef):
|
||||
class FourierNDQ(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"height": 4,
|
||||
"n_circles": 1000,
|
||||
"n_vectors": 1000,
|
||||
"run_time": 10,
|
||||
"arrow_config": {
|
||||
"tip_length": 0.1,
|
||||
@@ -535,7 +592,7 @@ class FourierNDQ(FourierOfTrebleClef):
|
||||
|
||||
class FourierGoogleG(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
"n_circles": 10,
|
||||
"n_vectors": 10,
|
||||
"height": 5,
|
||||
"g_colors": [
|
||||
"#4285F4",
|
||||
@@ -570,7 +627,7 @@ class FourierGoogleG(FourierOfTrebleClef):
|
||||
|
||||
class ExplainCircleAnimations(FourierCirclesScene):
|
||||
CONFIG = {
|
||||
"n_circles": 100,
|
||||
"n_vectors": 100,
|
||||
"center_point": 2 * DOWN,
|
||||
"n_top_circles": 9,
|
||||
"path_height": 3,
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part2.shared_constructs import *
|
||||
from active_projects.diffyq.part2.shared_constructs import *
|
||||
|
||||
|
||||
class TwoDBodyWithManyTemperatures(ThreeDScene):
|
||||
@@ -164,8 +164,12 @@ class BringTwoRodsTogether(Scene):
|
||||
"tick_frequency": 10,
|
||||
},
|
||||
},
|
||||
"y_labels": range(20, 100, 20),
|
||||
"graph_x_min": 0,
|
||||
"graph_x_max": 10,
|
||||
"midpoint": 5,
|
||||
"max_temp": 90,
|
||||
"min_temp": 10,
|
||||
"wait_time": 30,
|
||||
"default_n_rod_pieces": 20,
|
||||
"alpha": 1.0,
|
||||
@@ -185,10 +189,9 @@ class BringTwoRodsTogether(Scene):
|
||||
|
||||
y_label = axes.get_y_axis_label("\\text{Temperature}")
|
||||
y_label.to_edge(UP)
|
||||
axes.y_axis.label = y_label
|
||||
axes.y_axis.add(y_label)
|
||||
axes.y_axis.add_numbers(
|
||||
*range(20, 100, 20)
|
||||
)
|
||||
axes.y_axis.add_numbers(*self.y_labels)
|
||||
|
||||
self.axes = axes
|
||||
self.y_label = y_label
|
||||
@@ -199,7 +202,7 @@ class BringTwoRodsTogether(Scene):
|
||||
x_min=self.graph_x_min,
|
||||
x_max=self.graph_x_max,
|
||||
step_size=self.step_size,
|
||||
discontinuities=[5],
|
||||
discontinuities=[self.midpoint],
|
||||
)
|
||||
graph.color_using_background_image("VerticalTempGradient")
|
||||
|
||||
@@ -314,22 +317,29 @@ class BringTwoRodsTogether(Scene):
|
||||
rods.add_updater(self.update_rods)
|
||||
|
||||
self.play(
|
||||
ClockPassesTime(
|
||||
self.clock,
|
||||
run_time=self.wait_time,
|
||||
hours_passed=self.wait_time,
|
||||
),
|
||||
self.get_clock_anim(self.wait_time),
|
||||
FadeOut(labels)
|
||||
)
|
||||
|
||||
#
|
||||
def initial_function(self, x):
|
||||
if x <= 5:
|
||||
return 90
|
||||
else:
|
||||
return 10
|
||||
def get_clock_anim(self, time, **kwargs):
|
||||
config = {
|
||||
"run_time": time,
|
||||
"hours_passed": time,
|
||||
}
|
||||
config.update(kwargs)
|
||||
return ClockPassesTime(self.clock, **config)
|
||||
|
||||
def update_graph(self, graph, dt, alpha=None, n_mini_steps=100):
|
||||
def initial_function(self, x):
|
||||
epsilon = 1e-10
|
||||
if x < self.midpoint - epsilon:
|
||||
return self.max_temp
|
||||
elif x > self.midpoint + epsilon:
|
||||
return self.min_temp
|
||||
else:
|
||||
return (self.min_temp + self.max_temp) / 2
|
||||
|
||||
def update_graph(self, graph, dt, alpha=None, n_mini_steps=500):
|
||||
if alpha is None:
|
||||
alpha = self.alpha
|
||||
points = np.append(
|
||||
@@ -349,16 +359,16 @@ class BringTwoRodsTogether(Scene):
|
||||
if (0 < i < len(points) - 1):
|
||||
second_deriv = d2y / (dx**2)
|
||||
else:
|
||||
second_deriv = 0.5 * d2y / dx
|
||||
second_deriv = 0
|
||||
second_deriv = 2 * d2y / (dx**2)
|
||||
# second_deriv = 0
|
||||
|
||||
y_change[i] = alpha * second_deriv * dt / n_mini_steps
|
||||
|
||||
# y_change[0] = y_change[1]
|
||||
# y_change[-1] = y_change[-2]
|
||||
y_change[0] = 0
|
||||
y_change[-1] = 0
|
||||
y_change -= np.mean(y_change)
|
||||
# y_change[0] = 0
|
||||
# y_change[-1] = 0
|
||||
# y_change -= np.mean(y_change)
|
||||
points[:, 1] += y_change
|
||||
graph.set_points_smoothly(points)
|
||||
return graph
|
||||
@@ -374,8 +384,17 @@ class BringTwoRodsTogether(Scene):
|
||||
)[1]
|
||||
for alt_x in (x - dx, x, x + dx)
|
||||
]
|
||||
d2y = ry - 2 * y + ly
|
||||
return d2y / (dx**2)
|
||||
|
||||
# At the boundary, don't return the second deriv,
|
||||
# but instead something matching the Neumann
|
||||
# boundary condition.
|
||||
if x == x_max:
|
||||
return (ly - y) / dx
|
||||
elif x == x_min:
|
||||
return (ry - y) / dx
|
||||
else:
|
||||
d2y = ry - 2 * y + ly
|
||||
return d2y / (dx**2)
|
||||
|
||||
def get_rod(self, x_min, x_max, n_pieces=None):
|
||||
if n_pieces is None:
|
||||
@@ -407,7 +426,7 @@ class BringTwoRodsTogether(Scene):
|
||||
self.rod_point_to_color(piece.get_right()),
|
||||
])
|
||||
|
||||
def rod_point_to_color(self, point):
|
||||
def rod_point_to_graph_y(self, point):
|
||||
axes = self.axes
|
||||
x = axes.x_axis.p2n(point)
|
||||
|
||||
@@ -417,11 +436,19 @@ class BringTwoRodsTogether(Scene):
|
||||
self.graph_x_max,
|
||||
x,
|
||||
)
|
||||
y = axes.y_axis.p2n(
|
||||
return axes.y_axis.p2n(
|
||||
graph.point_from_proportion(alpha)
|
||||
)
|
||||
return temperature_to_color(
|
||||
(y - 45) / 45
|
||||
|
||||
def y_to_color(self, y):
|
||||
y_max = self.max_temp
|
||||
y_min = self.min_temp
|
||||
alpha = inverse_interpolate(y_min, y_max, y)
|
||||
return temperature_to_color(interpolate(-0.8, 0.8, alpha))
|
||||
|
||||
def rod_point_to_color(self, point):
|
||||
return self.y_to_color(
|
||||
self.rod_point_to_graph_y(point)
|
||||
)
|
||||
|
||||
|
||||
@@ -450,6 +477,7 @@ class ShowEvolvingTempGraphWithArrows(BringTwoRodsTogether):
|
||||
self.add_clock()
|
||||
self.add_rod()
|
||||
self.add_arrows()
|
||||
self.initialize_updaters()
|
||||
self.let_play()
|
||||
|
||||
def add_axes(self):
|
||||
@@ -467,7 +495,10 @@ class ShowEvolvingTempGraphWithArrows(BringTwoRodsTogether):
|
||||
self.time_label.next_to(self.clock, DOWN)
|
||||
|
||||
def add_rod(self):
|
||||
rod = self.rod = self.get_rod(0, 10)
|
||||
rod = self.rod = self.get_rod(
|
||||
self.graph_x_min,
|
||||
self.graph_x_max,
|
||||
)
|
||||
self.add(rod)
|
||||
|
||||
def add_arrows(self):
|
||||
@@ -504,24 +535,25 @@ class ShowEvolvingTempGraphWithArrows(BringTwoRodsTogether):
|
||||
self.add(arrows)
|
||||
self.arrows = arrows
|
||||
|
||||
def initialize_updaters(self):
|
||||
if hasattr(self, "graph"):
|
||||
self.graph.add_updater(self.update_graph)
|
||||
if hasattr(self, "rod"):
|
||||
self.rod.add_updater(self.color_rod_by_graph)
|
||||
if hasattr(self, "time_label"):
|
||||
self.time_label.add_updater(
|
||||
lambda d, dt: d.increment_value(dt)
|
||||
)
|
||||
|
||||
def let_play(self):
|
||||
graph = self.graph
|
||||
rod = self.rod
|
||||
clock = self.clock
|
||||
time_label = self.time_label
|
||||
self.run_clock(self.wait_time)
|
||||
|
||||
graph.add_updater(self.update_graph)
|
||||
time_label.add_updater(
|
||||
lambda d, dt: d.increment_value(dt)
|
||||
)
|
||||
rod.add_updater(self.color_rod_by_graph)
|
||||
|
||||
# return
|
||||
def run_clock(self, time):
|
||||
self.play(
|
||||
ClockPassesTime(
|
||||
clock,
|
||||
run_time=self.wait_time,
|
||||
hours_passed=self.wait_time,
|
||||
self.clock,
|
||||
run_time=time,
|
||||
hours_passed=time,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part2.wordy_scenes import WriteHeatEquationTemplate
|
||||
from active_projects.diffyq.part2.wordy_scenes import WriteHeatEquationTemplate
|
||||
|
||||
|
||||
class ReactionsToInitialHeatEquation(PiCreatureScene):
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.ode.part1.staging import TourOfDifferentialEquations
|
||||
from active_projects.diffyq.part1.staging import TourOfDifferentialEquations
|
||||
|
||||
|
||||
class PartTwoOfTour(TourOfDifferentialEquations):
|
||||
@@ -210,7 +210,7 @@ class BlackScholes(AltBrownianMotion):
|
||||
self.wait(self.wait_time)
|
||||
|
||||
def add_title(self):
|
||||
title = TextMobject("Black-Sholes equations")
|
||||
title = TextMobject("Black-Scholes equations")
|
||||
title.scale(1.5)
|
||||
title.next_to(2 * UP, UP)
|
||||
|
||||
@@ -235,7 +235,7 @@ class BlackScholes(AltBrownianMotion):
|
||||
x_max=20,
|
||||
y_min=0,
|
||||
y_max=10,
|
||||
number_line_config={
|
||||
axis_config={
|
||||
"unit_size": 0.5,
|
||||
},
|
||||
)
|
||||
303
from_3b1b/active/diffyq/part3/discrete_case.py
Normal file
303
from_3b1b/active/diffyq/part3/discrete_case.py
Normal file
@@ -0,0 +1,303 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.diffyq.part2.heat_equation import *
|
||||
|
||||
|
||||
class ShowNewRuleAtDiscreteBoundary(DiscreteSetup):
|
||||
CONFIG = {
|
||||
"axes_config": {
|
||||
"x_min": 0,
|
||||
"stroke_width": 1,
|
||||
"x_axis_config": {
|
||||
"include_tip": False,
|
||||
},
|
||||
},
|
||||
"freq_amplitude_pairs": [
|
||||
(1, 0.5),
|
||||
(2, 1),
|
||||
(3, 0.5),
|
||||
(4, 0.3),
|
||||
],
|
||||
"v_line_class": DashedLine,
|
||||
"v_line_config": {
|
||||
|
||||
},
|
||||
"step_size": 1,
|
||||
"wait_time": 15,
|
||||
"alpha": 0.25,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_axes()
|
||||
self.set_points()
|
||||
self.show_boundary_point_influenced_by_neighbor()
|
||||
self.add_clock()
|
||||
self.let_evolve()
|
||||
|
||||
def set_points(self):
|
||||
axes = self.axes
|
||||
for mob in axes.family_members_with_points():
|
||||
if isinstance(mob, Line):
|
||||
mob.set_stroke(width=1)
|
||||
|
||||
step_size = self.step_size
|
||||
xs = np.arange(
|
||||
axes.x_min,
|
||||
axes.x_max + step_size,
|
||||
step_size
|
||||
)
|
||||
|
||||
dots = self.dots = self.get_dots(axes, xs)
|
||||
self.v_lines = self.get_v_lines(dots)
|
||||
self.rod_pieces = self.get_rod_pieces(dots)
|
||||
|
||||
# rod_pieces
|
||||
|
||||
self.add(self.dots)
|
||||
self.add(self.v_lines)
|
||||
self.add(self.rod_pieces)
|
||||
|
||||
def show_boundary_point_influenced_by_neighbor(self):
|
||||
dots = self.dots
|
||||
ld = dots[0]
|
||||
ld_in = dots[1]
|
||||
rd = dots[-1]
|
||||
rd_in = dots[-2]
|
||||
v_len = 0.75
|
||||
l_arrow = Vector(v_len * LEFT)
|
||||
l_arrow.move_to(ld.get_left(), RIGHT)
|
||||
r_arrow = Vector(v_len * RIGHT)
|
||||
r_arrow.move_to(rd.get_right(), LEFT)
|
||||
arrows = VGroup(l_arrow, r_arrow)
|
||||
q_marks = VGroup(*[
|
||||
TexMobject("?").scale(1.5).next_to(
|
||||
arrow, arrow.get_vector()
|
||||
)
|
||||
for arrow in arrows
|
||||
])
|
||||
|
||||
arrows.set_color(YELLOW)
|
||||
q_marks.set_color(YELLOW)
|
||||
|
||||
blocking_rects = VGroup(*[
|
||||
BackgroundRectangle(VGroup(
|
||||
*dots[i:-i],
|
||||
*self.rod_pieces[i:-i]
|
||||
))
|
||||
for i in [1, 2]
|
||||
])
|
||||
for rect in blocking_rects:
|
||||
rect.stretch(1.1, dim=1, about_edge=UP)
|
||||
|
||||
self.play(FadeIn(blocking_rects[0]))
|
||||
self.play(
|
||||
LaggedStartMap(ShowCreation, arrows),
|
||||
LaggedStart(*[
|
||||
FadeInFrom(q_mark, -arrow.get_vector())
|
||||
for q_mark, arrow in zip(q_marks, arrows)
|
||||
]),
|
||||
run_time=1.5
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# Point to inward neighbor
|
||||
new_arrows = VGroup(*[
|
||||
Arrow(
|
||||
d1.get_center(),
|
||||
VGroup(d1, d2).get_center(),
|
||||
buff=0,
|
||||
).match_style(l_arrow)
|
||||
for d1, d2 in [(ld, ld_in), (rd, rd_in)]
|
||||
])
|
||||
new_arrows.match_style(arrows)
|
||||
|
||||
l_brace = Brace(VGroup(ld, ld_in), DOWN)
|
||||
r_brace = Brace(VGroup(rd, rd_in), DOWN)
|
||||
braces = VGroup(l_brace, r_brace)
|
||||
for brace in braces:
|
||||
brace.align_to(
|
||||
self.axes.x_axis.get_center(), UP
|
||||
)
|
||||
brace.shift(SMALL_BUFF * DOWN)
|
||||
brace.add(brace.get_tex("\\Delta x"))
|
||||
|
||||
self.play(
|
||||
ReplacementTransform(arrows, new_arrows),
|
||||
FadeOut(q_marks),
|
||||
ReplacementTransform(*blocking_rects)
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeInFrom(braces, UP))
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(new_arrows),
|
||||
FadeOut(blocking_rects[1]),
|
||||
FadeOut(braces),
|
||||
)
|
||||
|
||||
def add_clock(self):
|
||||
super().add_clock()
|
||||
self.time_label.add_updater(
|
||||
lambda d, dt: d.increment_value(dt)
|
||||
)
|
||||
VGroup(
|
||||
self.clock,
|
||||
self.time_label
|
||||
).shift(2 * LEFT)
|
||||
|
||||
def let_evolve(self):
|
||||
dots = self.dots
|
||||
dots.add_updater(self.update_dots)
|
||||
|
||||
wait_time = self.wait_time
|
||||
self.play(
|
||||
ClockPassesTime(
|
||||
self.clock,
|
||||
run_time=wait_time,
|
||||
hours_passed=wait_time,
|
||||
),
|
||||
)
|
||||
|
||||
#
|
||||
|
||||
def get_dots(self, axes, xs):
|
||||
dots = VGroup(*[
|
||||
Dot(axes.c2p(x, self.temp_func(x, 0)))
|
||||
for x in xs
|
||||
])
|
||||
|
||||
max_width = 0.8 * self.step_size
|
||||
for dot in dots:
|
||||
dot.add_updater(self.update_dot_color)
|
||||
if dot.get_width() > max_width:
|
||||
dot.set_width(max_width)
|
||||
|
||||
return dots
|
||||
|
||||
def get_v_lines(self, dots):
|
||||
return always_redraw(lambda: VGroup(*[
|
||||
self.get_v_line(dot)
|
||||
for dot in dots
|
||||
]))
|
||||
|
||||
def get_v_line(self, dot):
|
||||
x_axis = self.axes.x_axis
|
||||
bottom = dot.get_bottom()
|
||||
x = x_axis.p2n(bottom)
|
||||
proj_point = x_axis.n2p(x)
|
||||
return self.v_line_class(
|
||||
proj_point, bottom,
|
||||
**self.v_line_config,
|
||||
)
|
||||
|
||||
def get_rod_pieces(self, dots):
|
||||
axis = self.axes.x_axis
|
||||
factor = 1 - np.exp(-(0.8 / self.step_size)**2)
|
||||
width = factor * self.step_size
|
||||
|
||||
pieces = VGroup()
|
||||
for dot in dots:
|
||||
piece = Line(ORIGIN, width * RIGHT)
|
||||
piece.set_stroke(width=5)
|
||||
piece.move_to(dot)
|
||||
piece.set_y(axis.get_center()[1])
|
||||
piece.dot = dot
|
||||
piece.add_updater(
|
||||
lambda p: p.match_color(p.dot)
|
||||
)
|
||||
pieces.add(piece)
|
||||
return pieces
|
||||
|
||||
def update_dot_color(self, dot):
|
||||
y = self.axes.y_axis.p2n(dot.get_center())
|
||||
dot.set_color(self.y_to_color(y))
|
||||
|
||||
def update_dots(self, dots, dt):
|
||||
for ds in zip(dots, dots[1:], dots[2:]):
|
||||
points = [d.get_center() for d in ds]
|
||||
x0, x1, x2 = [p[0] for p in points]
|
||||
dx = x1 - x0
|
||||
y0, y1, y2 = [p[1] for p in points]
|
||||
|
||||
self.update_dot(
|
||||
dot=ds[1],
|
||||
dt=dt,
|
||||
mean_diff=0.5 * (y2 - 2 * y1 + y0) / dx
|
||||
)
|
||||
if ds[0] is dots[0]:
|
||||
self.update_dot(
|
||||
dot=ds[0],
|
||||
dt=dt,
|
||||
mean_diff=(y1 - y0) / dx
|
||||
)
|
||||
elif ds[-1] is dots[-1]:
|
||||
self.update_dot(
|
||||
dot=ds[-1],
|
||||
dt=dt,
|
||||
mean_diff=(y1 - y2) / dx
|
||||
)
|
||||
|
||||
def update_dot(self, dot, dt, mean_diff):
|
||||
dot.shift(mean_diff * self.alpha * dt * UP)
|
||||
|
||||
|
||||
class DiscreteEvolutionPoint25(ShowNewRuleAtDiscreteBoundary):
|
||||
CONFIG = {
|
||||
"step_size": 0.25,
|
||||
"alpha": 0.5,
|
||||
"wait_time": 30,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_axes()
|
||||
self.set_points()
|
||||
self.add_clock()
|
||||
self.let_evolve()
|
||||
|
||||
|
||||
class DiscreteEvolutionPoint1(DiscreteEvolutionPoint25):
|
||||
CONFIG = {
|
||||
"step_size": 0.1,
|
||||
"v_line_config": {
|
||||
"stroke_width": 1,
|
||||
},
|
||||
"wait_time": 30,
|
||||
}
|
||||
|
||||
|
||||
class FlatEdgesForDiscreteEvolution(DiscreteEvolutionPoint1):
|
||||
CONFIG = {
|
||||
"wait_time": 20,
|
||||
"step_size": 0.1,
|
||||
}
|
||||
|
||||
def let_evolve(self):
|
||||
lines = VGroup(*[
|
||||
Line(LEFT, RIGHT)
|
||||
for x in range(2)
|
||||
])
|
||||
lines.set_width(1.5)
|
||||
lines.set_stroke(WHITE, 5, opacity=0.5)
|
||||
lines.add_updater(self.update_lines)
|
||||
|
||||
turn_animation_into_updater(
|
||||
ShowCreation(lines, run_time=2)
|
||||
)
|
||||
self.add(lines)
|
||||
|
||||
super().let_evolve()
|
||||
|
||||
def update_lines(self, lines):
|
||||
dots = self.dots
|
||||
for line, dot in zip(lines, [dots[0], dots[-1]]):
|
||||
line.move_to(dot)
|
||||
|
||||
|
||||
class FlatEdgesForDiscreteEvolutionTinySteps(FlatEdgesForDiscreteEvolution):
|
||||
CONFIG = {
|
||||
"step_size": 0.025,
|
||||
"wait_time": 10,
|
||||
"v_line_class": Line,
|
||||
"v_line_config": {
|
||||
"stroke_opacity": 0.5,
|
||||
}
|
||||
}
|
||||
175
from_3b1b/active/diffyq/part3/pi_creature_scenes.py
Normal file
175
from_3b1b/active/diffyq/part3/pi_creature_scenes.py
Normal file
@@ -0,0 +1,175 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.diffyq.part2.wordy_scenes import *
|
||||
|
||||
|
||||
class IveHeardOfThis(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
point = VectorizedPoint()
|
||||
point.move_to(3 * RIGHT + 2 * UP)
|
||||
self.student_says(
|
||||
"I've heard\\\\", "of this!",
|
||||
student_index=1,
|
||||
target_mode="hooray",
|
||||
bubble_kwargs={
|
||||
"height": 3,
|
||||
"width": 3,
|
||||
"direction": RIGHT,
|
||||
},
|
||||
run_time=1,
|
||||
)
|
||||
self.change_student_modes(
|
||||
"thinking", "hooray", "thinking",
|
||||
look_at_arg=point,
|
||||
added_anims=[self.teacher.change, "happy"]
|
||||
)
|
||||
self.wait(3)
|
||||
self.student_says(
|
||||
"But who\\\\", "cares?",
|
||||
student_index=1,
|
||||
target_mode="maybe",
|
||||
bubble_kwargs={
|
||||
"direction": RIGHT,
|
||||
"width": 3,
|
||||
"height": 3,
|
||||
},
|
||||
run_time=1,
|
||||
)
|
||||
self.change_student_modes(
|
||||
"pondering", "maybe", "pondering",
|
||||
look_at_arg=point,
|
||||
added_anims=[self.teacher.change, "guilty"]
|
||||
)
|
||||
self.wait(5)
|
||||
|
||||
|
||||
class InFouriersShoes(PiCreatureScene, WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
randy = self.pi_creature
|
||||
fourier = ImageMobject("Joseph Fourier")
|
||||
fourier.set_height(4)
|
||||
fourier.next_to(randy, RIGHT, LARGE_BUFF)
|
||||
fourier.align_to(randy, DOWN)
|
||||
|
||||
equation = self.get_d1_equation()
|
||||
equation.next_to(fourier, UP, MED_LARGE_BUFF)
|
||||
|
||||
decades = list(range(1740, 2040, 20))
|
||||
time_line = NumberLine(
|
||||
x_min=decades[0],
|
||||
x_max=decades[-1],
|
||||
tick_frequency=1,
|
||||
tick_size=0.05,
|
||||
longer_tick_multiple=4,
|
||||
unit_size=0.2,
|
||||
numbers_with_elongated_ticks=decades,
|
||||
numbers_to_show=decades,
|
||||
decimal_number_config={
|
||||
"group_with_commas": False,
|
||||
},
|
||||
stroke_width=2,
|
||||
)
|
||||
time_line.add_numbers()
|
||||
time_line.move_to(ORIGIN, RIGHT)
|
||||
time_line.to_edge(UP)
|
||||
triangle = ArrowTip(start_angle=-90 * DEGREES)
|
||||
triangle.set_height(0.25)
|
||||
triangle.move_to(time_line.n2p(2019), DOWN)
|
||||
triangle.set_color(WHITE)
|
||||
|
||||
self.play(FadeInFrom(fourier, 2 * LEFT))
|
||||
self.play(randy.change, "pondering")
|
||||
self.wait()
|
||||
self.play(
|
||||
DrawBorderThenFill(triangle, run_time=1),
|
||||
FadeInFromDown(equation),
|
||||
FadeIn(time_line),
|
||||
)
|
||||
self.play(
|
||||
Animation(triangle),
|
||||
ApplyMethod(
|
||||
time_line.shift,
|
||||
time_line.n2p(2019) - time_line.n2p(1822),
|
||||
run_time=5
|
||||
),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class SineCurveIsUnrealistic(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.student_says(
|
||||
"But that would\\\\never happen!",
|
||||
student_index=1,
|
||||
bubble_kwargs={
|
||||
"direction": RIGHT,
|
||||
"height": 3,
|
||||
"width": 4,
|
||||
},
|
||||
target_mode="angry"
|
||||
)
|
||||
self.change_student_modes(
|
||||
"guilty", "angry", "hesitant",
|
||||
added_anims=[
|
||||
self.teacher.change, "tease"
|
||||
]
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(
|
||||
RemovePiCreatureBubble(self.students[1]),
|
||||
self.teacher.change, "raise_right_hand"
|
||||
)
|
||||
self.change_all_student_modes(
|
||||
"pondering",
|
||||
look_at_arg=3 * UP,
|
||||
)
|
||||
self.wait(5)
|
||||
|
||||
|
||||
class IfOnly(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.teacher_says(
|
||||
"If only!",
|
||||
target_mode="angry"
|
||||
)
|
||||
self.change_all_student_modes(
|
||||
"confused",
|
||||
look_at_arg=self.screen
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class SoWeGotNowhere(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.student_says(
|
||||
"So we've gotten\\\\nowhere!",
|
||||
target_mode="angry",
|
||||
added_anims=[
|
||||
self.teacher.change, "guilty"
|
||||
]
|
||||
)
|
||||
self.change_all_student_modes("angry")
|
||||
self.wait()
|
||||
text = TexMobject(
|
||||
"&\\text{Actually,}\\\\",
|
||||
"&\\sin\\left({x}\\right)"
|
||||
"e^{-\\alpha {t}}\\\\",
|
||||
"&\\text{isn't far off.}",
|
||||
tex_to_color_map={
|
||||
"{x}": GREEN,
|
||||
"{t}": YELLOW,
|
||||
}
|
||||
)
|
||||
text.scale(0.8)
|
||||
self.teacher_says(
|
||||
text,
|
||||
content_introduction_class=FadeIn,
|
||||
bubble_kwargs={
|
||||
"width": 4,
|
||||
"height": 3.5,
|
||||
}
|
||||
)
|
||||
self.change_all_student_modes(
|
||||
"pondering",
|
||||
look_at_arg=self.screen
|
||||
)
|
||||
self.wait(3)
|
||||
1243
from_3b1b/active/diffyq/part3/staging.py
Normal file
1243
from_3b1b/active/diffyq/part3/staging.py
Normal file
File diff suppressed because it is too large
Load Diff
2830
from_3b1b/active/diffyq/part3/temperature_graphs.py
Normal file
2830
from_3b1b/active/diffyq/part3/temperature_graphs.py
Normal file
File diff suppressed because it is too large
Load Diff
919
from_3b1b/active/diffyq/part3/wordy_scenes.py
Normal file
919
from_3b1b/active/diffyq/part3/wordy_scenes.py
Normal file
@@ -0,0 +1,919 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.diffyq.part2.wordy_scenes import *
|
||||
|
||||
|
||||
class ThreeMainObservations(Scene):
|
||||
def construct(self):
|
||||
fourier = ImageMobject("Joseph Fourier")
|
||||
name = TextMobject("Joseph Fourier")
|
||||
name.match_width(fourier)
|
||||
name.next_to(fourier, DOWN, SMALL_BUFF)
|
||||
fourier.add(name)
|
||||
fourier.set_height(5)
|
||||
fourier.to_corner(DR)
|
||||
fourier.shift(LEFT)
|
||||
bubble = ThoughtBubble(
|
||||
direction=RIGHT,
|
||||
height=3,
|
||||
width=4,
|
||||
)
|
||||
bubble.move_tip_to(fourier.get_corner(UL) + 0.5 * DR)
|
||||
|
||||
observations = VGroup(
|
||||
TextMobject(
|
||||
"1)",
|
||||
"Sine = Nice",
|
||||
),
|
||||
TextMobject(
|
||||
"2)",
|
||||
"Linearity"
|
||||
),
|
||||
TextMobject(
|
||||
"3)",
|
||||
"Fourier series"
|
||||
),
|
||||
)
|
||||
# heart = SuitSymbol("hearts")
|
||||
# heart.replace(observations[0][2])
|
||||
# observations[0][2].become(heart)
|
||||
# observations[0][1].add(happiness)
|
||||
# observations[2][2].align_to(
|
||||
# observations[2][1], LEFT,
|
||||
# )
|
||||
|
||||
observations.arrange(
|
||||
DOWN,
|
||||
aligned_edge=LEFT,
|
||||
buff=2 * LARGE_BUFF,
|
||||
)
|
||||
observations.set_height(FRAME_HEIGHT - 2)
|
||||
observations.to_corner(UL, buff=LARGE_BUFF)
|
||||
|
||||
self.add(fourier)
|
||||
self.play(ShowCreation(bubble))
|
||||
self.wait()
|
||||
self.play(LaggedStart(*[
|
||||
TransformFromCopy(bubble, observation[0])
|
||||
for observation in observations
|
||||
], lag_ratio=0.2))
|
||||
self.play(
|
||||
FadeOut(fourier),
|
||||
FadeOut(bubble),
|
||||
)
|
||||
self.wait()
|
||||
for obs in observations:
|
||||
self.play(FadeInFrom(obs[1], LEFT))
|
||||
self.wait()
|
||||
|
||||
|
||||
class LastChapterWrapper(Scene):
|
||||
def construct(self):
|
||||
full_rect = FullScreenFadeRectangle(
|
||||
fill_color=DARK_GREY,
|
||||
fill_opacity=1,
|
||||
)
|
||||
rect = ScreenRectangle(height=6)
|
||||
rect.set_stroke(WHITE, 2)
|
||||
rect.set_fill(BLACK, 1)
|
||||
title = TextMobject("Last chapter")
|
||||
title.scale(2)
|
||||
title.to_edge(UP)
|
||||
rect.next_to(title, DOWN)
|
||||
|
||||
self.add(full_rect)
|
||||
self.play(
|
||||
FadeIn(rect),
|
||||
Write(title, run_time=2),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ThreeConstraints(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
self.cross_out_solving()
|
||||
self.show_three_conditions()
|
||||
|
||||
def cross_out_solving(self):
|
||||
equation = self.get_d1_equation()
|
||||
words = TextMobject("Solve this equation")
|
||||
words.to_edge(UP)
|
||||
equation.next_to(words, DOWN)
|
||||
cross = Cross(words)
|
||||
|
||||
self.add(words, equation)
|
||||
self.wait()
|
||||
self.play(ShowCreation(cross))
|
||||
self.wait()
|
||||
|
||||
self.equation = equation
|
||||
self.to_remove = VGroup(words, cross)
|
||||
|
||||
def show_three_conditions(self):
|
||||
equation = self.equation
|
||||
to_remove = self.to_remove
|
||||
|
||||
title = TexMobject(
|
||||
"\\text{Constraints }"
|
||||
"T({x}, {t})"
|
||||
"\\text{ must satisfy:}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
title.to_edge(UP)
|
||||
|
||||
items = VGroup(
|
||||
TextMobject("1)", "The PDE"),
|
||||
TextMobject("2)", "Boundary condition"),
|
||||
TextMobject("3)", "Initial condition"),
|
||||
)
|
||||
items.scale(0.7)
|
||||
items.arrange(RIGHT, buff=LARGE_BUFF)
|
||||
items.set_width(FRAME_WIDTH - 2)
|
||||
items.next_to(title, DOWN, LARGE_BUFF)
|
||||
items[1].set_color(MAROON_B)
|
||||
items[2].set_color(RED)
|
||||
|
||||
bc_paren = TextMobject("(Explained soon)")
|
||||
bc_paren.scale(0.7)
|
||||
bc_paren.next_to(items[1], DOWN)
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(title),
|
||||
FadeOutAndShift(to_remove, UP),
|
||||
equation.scale, 0.6,
|
||||
equation.next_to, items[0], DOWN,
|
||||
equation.shift_onto_screen,
|
||||
LaggedStartMap(FadeIn, [
|
||||
items[0],
|
||||
items[1][0],
|
||||
items[2][0],
|
||||
])
|
||||
)
|
||||
self.wait()
|
||||
self.play(Write(items[1][1]))
|
||||
bc_paren.match_y(equation)
|
||||
self.play(FadeInFrom(bc_paren, UP))
|
||||
self.wait(2)
|
||||
self.play(Write(items[2][1]))
|
||||
self.wait(2)
|
||||
|
||||
self.title = title
|
||||
self.items = items
|
||||
self.pde = equation
|
||||
self.bc_paren = bc_paren
|
||||
|
||||
|
||||
class RectAroundEquation(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
eq = self.get_d1_equation()
|
||||
self.play(ShowCreationThenFadeAround(eq))
|
||||
|
||||
|
||||
class BorderRect(Scene):
|
||||
def construct(self):
|
||||
rect = FullScreenFadeRectangle()
|
||||
rect.set_stroke(WHITE, 3)
|
||||
rect.set_fill(opacity=0)
|
||||
self.add(rect)
|
||||
|
||||
|
||||
class SeekIdealized(Scene):
|
||||
def construct(self):
|
||||
phrases = VGroup(*[
|
||||
TextMobject(
|
||||
"Seek", text, "problems",
|
||||
tex_to_color_map={
|
||||
"realistic": GREEN,
|
||||
"{idealized}": YELLOW,
|
||||
"over-idealized": YELLOW,
|
||||
"general": BLUE,
|
||||
}
|
||||
)
|
||||
for text in [
|
||||
"realistic",
|
||||
"{idealized}",
|
||||
"over-idealized",
|
||||
"general",
|
||||
]
|
||||
])
|
||||
phrases.scale(2)
|
||||
words = VGroup()
|
||||
for phrase in phrases:
|
||||
phrase.center()
|
||||
word = phrase[1]
|
||||
words.add(word)
|
||||
phrase.remove(word)
|
||||
arrow = Vector(DOWN)
|
||||
arrow.set_stroke(WHITE, 6)
|
||||
arrow.next_to(words[3], UP)
|
||||
low_arrow = arrow.copy()
|
||||
low_arrow.next_to(words[3], DOWN)
|
||||
|
||||
solutions = TextMobject("solutions")
|
||||
solutions.scale(2)
|
||||
solutions.move_to(phrases[3][1], UL)
|
||||
models = TextMobject("models")
|
||||
models.scale(2)
|
||||
models.next_to(
|
||||
words[0], RIGHT, buff=0.35,
|
||||
aligned_edge=DOWN
|
||||
)
|
||||
|
||||
phrases.center()
|
||||
phrase = phrases[0]
|
||||
|
||||
self.add(phrase)
|
||||
self.add(words[0])
|
||||
self.wait()
|
||||
words[0].save_state()
|
||||
self.play(
|
||||
words[0].to_edge, DOWN,
|
||||
words[0].set_opacity, 0.5,
|
||||
Transform(phrase, phrases[1]),
|
||||
FadeInFrom(words[1], UP)
|
||||
)
|
||||
self.wait()
|
||||
# self.play(
|
||||
# words[1].move_to, words[2], RIGHT,
|
||||
# FadeIn(words[2]),
|
||||
# Transform(phrase, phrases[2])
|
||||
# )
|
||||
# self.wait()
|
||||
self.play(
|
||||
words[1].next_to, arrow, UP,
|
||||
ShowCreation(arrow),
|
||||
MaintainPositionRelativeTo(
|
||||
phrase, words[1]
|
||||
),
|
||||
FadeInFrom(solutions, LEFT),
|
||||
FadeIn(words[3]),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
words[0].generate_target()
|
||||
words[0].target.next_to(low_arrow, DOWN)
|
||||
words[0].target.set_opacity(1)
|
||||
models.shift(
|
||||
words[0].target.get_center() -
|
||||
words[0].saved_state.get_center()
|
||||
)
|
||||
self.play(
|
||||
MoveToTarget(words[0]),
|
||||
ShowCreation(low_arrow),
|
||||
FadeInFrom(models, LEFT)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class SecondDerivativeOfSine(Scene):
|
||||
def construct(self):
|
||||
equation = TexMobject(
|
||||
"{d^2 \\over d{x}^2}",
|
||||
"\\cos\\left({x}\\right) =",
|
||||
"-\\cos\\left({x}\\right)",
|
||||
tex_to_color_map={
|
||||
"{x}": GREEN,
|
||||
}
|
||||
)
|
||||
|
||||
self.add(equation)
|
||||
|
||||
|
||||
class EquationAboveSineAnalysis(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
equation = self.get_d1_equation()
|
||||
equation.to_edge(UP)
|
||||
equation.shift(2 * LEFT)
|
||||
eq_index = equation.index_of_part_by_tex("=")
|
||||
lhs = equation[:eq_index]
|
||||
eq = equation[eq_index]
|
||||
rhs = equation[eq_index + 1:]
|
||||
t_terms = equation.get_parts_by_tex("{t}")[1:]
|
||||
t_terms.save_state()
|
||||
zeros = VGroup(*[
|
||||
TexMobject("0").replace(t, dim_to_match=1)
|
||||
for t in t_terms
|
||||
])
|
||||
zeros.align_to(t_terms, DOWN)
|
||||
new_rhs = TexMobject(
|
||||
"=", "-\\alpha \\cdot {T}", "({x}, 0)",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
# new_rhs.move_to(equation.get_right())
|
||||
# new_rhs.next_to(equation, DOWN, MED_LARGE_BUFF)
|
||||
# new_rhs.align_to(eq, LEFT)
|
||||
new_rhs.next_to(equation, RIGHT)
|
||||
new_rhs.shift(SMALL_BUFF * DOWN)
|
||||
|
||||
self.add(equation)
|
||||
self.play(ShowCreationThenFadeAround(rhs))
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOutAndShift(t_terms, UP),
|
||||
FadeInFrom(zeros, DOWN),
|
||||
)
|
||||
t_terms.fade(1)
|
||||
self.wait()
|
||||
self.play(
|
||||
# VGroup(equation, zeros).next_to,
|
||||
# new_rhs, LEFT,
|
||||
FadeIn(new_rhs),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
VGroup(
|
||||
lhs[6:],
|
||||
eq,
|
||||
rhs,
|
||||
new_rhs[0],
|
||||
new_rhs[-3:],
|
||||
zeros,
|
||||
).fade, 0.5,
|
||||
)
|
||||
self.play(ShowCreationThenFadeAround(lhs[:6]))
|
||||
self.play(ShowCreationThenFadeAround(new_rhs[1:-3]))
|
||||
self.wait()
|
||||
|
||||
|
||||
class ExpVideoWrapper(Scene):
|
||||
def construct(self):
|
||||
self.add(FullScreenFadeRectangle(
|
||||
fill_color=DARKER_GREY,
|
||||
fill_opacity=1,
|
||||
))
|
||||
|
||||
screen = ImageMobject("eoc_chapter5_thumbnail")
|
||||
screen.set_height(6)
|
||||
rect = SurroundingRectangle(screen, buff=0)
|
||||
rect.set_stroke(WHITE, 2)
|
||||
screen.add(rect)
|
||||
title = TextMobject("Need a refresher?")
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
screen.next_to(title, DOWN)
|
||||
|
||||
screen.center()
|
||||
self.play(
|
||||
# FadeInFrom(title, LEFT),
|
||||
FadeInFrom(screen, DOWN),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ShowSinExpDerivatives(WriteHeatEquationTemplate):
|
||||
CONFIG = {
|
||||
"tex_mobject_config": {
|
||||
"tex_to_color_map": {
|
||||
"{0}": WHITE,
|
||||
"\\partial": WHITE,
|
||||
"=": WHITE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
pde = self.get_d1_equation_without_inputs()
|
||||
pde.to_edge(UP)
|
||||
pde.generate_target()
|
||||
|
||||
new_rhs = TexMobject(
|
||||
"=- \\alpha \\cdot T",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
new_rhs.next_to(pde, RIGHT)
|
||||
new_rhs.align_to(pde.get_part_by_tex("alpha"), DOWN)
|
||||
|
||||
equation1 = TexMobject(
|
||||
"T({x}, {0}) = \\sin\\left({x}\\right)",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
equation2 = TexMobject(
|
||||
"T({x}, {t}) = \\sin\\left({x}\\right)",
|
||||
"e^{-\\alpha{t}}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
for eq in equation1, equation2:
|
||||
eq.next_to(pde, DOWN, MED_LARGE_BUFF)
|
||||
|
||||
eq2_part1 = equation2[:len(equation1)]
|
||||
eq2_part2 = equation2[len(equation1):]
|
||||
|
||||
# Rectangles
|
||||
exp_rect = SurroundingRectangle(eq2_part2)
|
||||
exp_rect.set_stroke(RED, 3)
|
||||
sin_rect = SurroundingRectangle(
|
||||
eq2_part1[-3:]
|
||||
)
|
||||
sin_rect.set_color(BLUE)
|
||||
|
||||
VGroup(pde.target, new_rhs).center().to_edge(UP)
|
||||
|
||||
# Show proposed solution
|
||||
self.add(pde)
|
||||
self.add(equation1)
|
||||
self.wait()
|
||||
self.play(
|
||||
MoveToTarget(pde),
|
||||
FadeInFrom(new_rhs, LEFT)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
ReplacementTransform(equation1, eq2_part1),
|
||||
FadeIn(eq2_part2),
|
||||
)
|
||||
self.play(ShowCreation(exp_rect))
|
||||
self.wait()
|
||||
self.play(FadeOut(exp_rect))
|
||||
|
||||
# Take partial derivatives wrt x
|
||||
q_mark = TexMobject("?")
|
||||
q_mark.next_to(pde.get_part_by_tex("="), UP)
|
||||
q_mark.set_color(RED)
|
||||
|
||||
arrow1 = Vector(3 * DOWN + 1 * RIGHT, color=WHITE)
|
||||
arrow1.scale(1.2 / arrow1.get_length())
|
||||
arrow1.next_to(
|
||||
eq2_part2.get_corner(DL),
|
||||
DOWN, MED_LARGE_BUFF
|
||||
)
|
||||
ddx_label1 = TexMobject(
|
||||
"\\partial \\over \\partial {x}",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
ddx_label1.scale(0.7)
|
||||
ddx_label1.next_to(
|
||||
arrow1.point_from_proportion(0.8),
|
||||
UR, SMALL_BUFF
|
||||
)
|
||||
|
||||
pde_ddx = VGroup(
|
||||
*pde.get_parts_by_tex("\\partial")[2:],
|
||||
pde.get_parts_by_tex("\\over")[1],
|
||||
pde.get_part_by_tex("{x}"),
|
||||
)
|
||||
pde_ddx_rect = SurroundingRectangle(pde_ddx)
|
||||
pde_ddx_rect.set_color(GREEN)
|
||||
|
||||
eq2_part2_rect = SurroundingRectangle(eq2_part2)
|
||||
|
||||
dx = TexMobject(
|
||||
"\\cos\\left({x}\\right)", "e^{-\\alpha {t}}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
ddx = TexMobject(
|
||||
"-\\sin\\left({x}\\right)", "e^{-\\alpha {t}}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
dx.next_to(arrow1, DOWN)
|
||||
dx.align_to(eq2_part2, RIGHT)
|
||||
x_shift = arrow1.get_end()[0] - arrow1.get_start()[0]
|
||||
x_shift *= 2
|
||||
dx.shift(x_shift * RIGHT)
|
||||
arrow2 = arrow1.copy()
|
||||
arrow2.next_to(dx, DOWN)
|
||||
arrow2.shift(MED_SMALL_BUFF * RIGHT)
|
||||
dx_arrows = VGroup(arrow1, arrow2)
|
||||
|
||||
ddx_label2 = ddx_label1.copy()
|
||||
ddx_label2.shift(
|
||||
arrow2.get_center() - arrow1.get_center()
|
||||
)
|
||||
ddx.next_to(arrow2, DOWN)
|
||||
ddx.align_to(eq2_part2, RIGHT)
|
||||
ddx.shift(2 * x_shift * RIGHT)
|
||||
|
||||
rhs = equation2[-6:]
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(q_mark)
|
||||
)
|
||||
self.play(
|
||||
ShowCreation(pde_ddx_rect)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
LaggedStart(
|
||||
GrowArrow(arrow1),
|
||||
GrowArrow(arrow2),
|
||||
),
|
||||
TransformFromCopy(
|
||||
pde_ddx[0], ddx_label1
|
||||
),
|
||||
TransformFromCopy(
|
||||
pde_ddx[0], ddx_label2
|
||||
),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
TransformFromCopy(rhs, dx)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeIn(eq2_part2_rect)
|
||||
)
|
||||
self.play(
|
||||
Transform(
|
||||
eq2_part2_rect,
|
||||
SurroundingRectangle(dx[-3:])
|
||||
)
|
||||
)
|
||||
self.play(
|
||||
FadeOut(eq2_part2_rect)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
TransformFromCopy(dx, ddx)
|
||||
)
|
||||
self.play(
|
||||
FadeIn(
|
||||
SurroundingRectangle(ddx).match_style(
|
||||
pde_ddx_rect
|
||||
)
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# Take partial derivative wrt t
|
||||
pde_ddt = pde[:pde.index_of_part_by_tex("=") - 1]
|
||||
pde_ddt_rect = SurroundingRectangle(pde_ddt)
|
||||
|
||||
dt_arrow = Arrow(
|
||||
arrow1.get_start(),
|
||||
arrow2.get_end() + RIGHT,
|
||||
buff=0
|
||||
)
|
||||
dt_arrow.flip(UP)
|
||||
dt_arrow.next_to(dx_arrows, LEFT, MED_LARGE_BUFF)
|
||||
|
||||
dt_label = TexMobject(
|
||||
"\\partial \\over \\partial {t}",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
dt_label.scale(1)
|
||||
dt_label.next_to(
|
||||
dt_arrow.get_center(), UL,
|
||||
SMALL_BUFF,
|
||||
)
|
||||
|
||||
rhs_copy = rhs.copy()
|
||||
rhs_copy.next_to(dt_arrow.get_end(), DOWN)
|
||||
rhs_copy.shift(MED_LARGE_BUFF * LEFT)
|
||||
rhs_copy.match_y(ddx)
|
||||
|
||||
minus_alpha_in_exp = rhs_copy[-3][1:].copy()
|
||||
minus_alpha_in_exp.set_color(RED)
|
||||
minus_alpha = TexMobject("-\\alpha")
|
||||
minus_alpha.next_to(rhs_copy, LEFT)
|
||||
minus_alpha.align_to(rhs_copy[0][0], DOWN)
|
||||
dot = TexMobject("\\cdot")
|
||||
dot.move_to(midpoint(
|
||||
minus_alpha.get_right(),
|
||||
rhs_copy.get_left(),
|
||||
))
|
||||
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
pde_ddx_rect,
|
||||
pde_ddt_rect,
|
||||
)
|
||||
)
|
||||
self.play(
|
||||
GrowArrow(dt_arrow),
|
||||
TransformFromCopy(
|
||||
pde_ddt,
|
||||
dt_label,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
self.play(TransformFromCopy(rhs, rhs_copy))
|
||||
self.play(FadeIn(minus_alpha_in_exp))
|
||||
self.play(
|
||||
ApplyMethod(
|
||||
minus_alpha_in_exp.replace, minus_alpha,
|
||||
path_arc=TAU / 4
|
||||
),
|
||||
FadeIn(dot),
|
||||
)
|
||||
self.play(
|
||||
FadeIn(minus_alpha),
|
||||
FadeOut(minus_alpha_in_exp),
|
||||
)
|
||||
self.wait()
|
||||
rhs_copy.add(minus_alpha, dot)
|
||||
self.play(
|
||||
FadeIn(SurroundingRectangle(rhs_copy))
|
||||
)
|
||||
self.wait()
|
||||
|
||||
#
|
||||
checkmark = TexMobject("\\checkmark")
|
||||
checkmark.set_color(GREEN)
|
||||
checkmark.move_to(q_mark, DOWN)
|
||||
self.play(
|
||||
FadeInFromDown(checkmark),
|
||||
FadeOutAndShift(q_mark, UP)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class DerivativesOfLinearFunction(WriteHeatEquationTemplate):
|
||||
CONFIG = {
|
||||
"tex_mobject_config": {
|
||||
"tex_to_color_map": {
|
||||
"{c}": WHITE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
func = TexMobject(
|
||||
"T({x}, {t}) = {c} \\cdot {x}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
dx_T = TexMobject("{c}", **self.tex_mobject_config)
|
||||
ddx_T = TexMobject("0")
|
||||
dt_T = TexMobject("0")
|
||||
|
||||
for mob in func, dx_T, ddx_T, dt_T:
|
||||
mob.scale(1.5)
|
||||
|
||||
func.generate_target()
|
||||
|
||||
arrows = VGroup(*[
|
||||
Vector(1.5 * RIGHT, color=WHITE)
|
||||
for x in range(3)
|
||||
])
|
||||
dx_arrows = arrows[:2]
|
||||
dt_arrow = arrows[2]
|
||||
dt_arrow.rotate(-TAU / 4)
|
||||
dx_group = VGroup(
|
||||
func.target,
|
||||
dx_arrows[0],
|
||||
dx_T,
|
||||
dx_arrows[1],
|
||||
ddx_T,
|
||||
)
|
||||
dx_group.arrange(RIGHT)
|
||||
for arrow, char, vect in zip(arrows, "xxt", [UP, UP, RIGHT]):
|
||||
label = TexMobject(
|
||||
"\\partial \\over \\partial {%s}" % char,
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
label.scale(0.7)
|
||||
label.next_to(arrow.get_center(), vect)
|
||||
arrow.add(label)
|
||||
|
||||
dt_arrow.shift(
|
||||
func.target[-3:].get_bottom() + MED_SMALL_BUFF * DOWN -
|
||||
dt_arrow.get_start(),
|
||||
)
|
||||
dt_T.next_to(dt_arrow.get_end(), DOWN)
|
||||
|
||||
self.play(FadeInFromDown(func))
|
||||
self.wait()
|
||||
self.play(
|
||||
MoveToTarget(func),
|
||||
LaggedStartMap(Write, dx_arrows),
|
||||
run_time=1,
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(func[-3:], dx_T),
|
||||
path_arc=-TAU / 4,
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(dx_T, ddx_T),
|
||||
path_arc=-TAU / 4,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# dt
|
||||
self.play(Write(dt_arrow))
|
||||
self.play(
|
||||
TransformFromCopy(func[-3:], dt_T)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class FlatAtBoundaryWords(Scene):
|
||||
def construct(self):
|
||||
words = self.get_bc_words()
|
||||
self.play(Write(words))
|
||||
self.wait()
|
||||
|
||||
def get_bc_words(self):
|
||||
return TextMobject(
|
||||
"Flat at boundary\\\\"
|
||||
"for all", "${t}$", "$> 0$",
|
||||
)
|
||||
|
||||
|
||||
class WriteOutBoundaryCondition(FlatAtBoundaryWords, ThreeConstraints, MovingCameraScene):
|
||||
def construct(self):
|
||||
self.force_skipping()
|
||||
ThreeConstraints.construct(self)
|
||||
self.revert_to_original_skipping_status()
|
||||
|
||||
self.add_ic()
|
||||
self.write_bc_words()
|
||||
self.write_bc_equation()
|
||||
|
||||
def add_ic(self):
|
||||
image = ImageMobject("temp_initial_condition_example")
|
||||
image.set_width(3)
|
||||
border = SurroundingRectangle(image, buff=SMALL_BUFF)
|
||||
border.shift(SMALL_BUFF * UP)
|
||||
border.set_stroke(WHITE, 2)
|
||||
group = Group(image, border)
|
||||
group.next_to(self.items[2], DOWN)
|
||||
self.add(group)
|
||||
|
||||
def write_bc_words(self):
|
||||
bc_paren = self.bc_paren
|
||||
bc_words = self.get_bc_words()
|
||||
bc_words.match_width(self.items[1][1])
|
||||
bc_words.move_to(bc_paren, UP)
|
||||
bc_words.set_color_by_tex("{t}", YELLOW)
|
||||
|
||||
self.play(ShowCreationThenFadeAround(
|
||||
VGroup(self.items[0], self.pde)
|
||||
))
|
||||
self.play(
|
||||
FadeOutAndShift(bc_paren, UP),
|
||||
FadeInFrom(bc_words, DOWN),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.bc_words = bc_words
|
||||
|
||||
def write_bc_equation(self):
|
||||
bc_words = self.bc_words
|
||||
|
||||
equation = TexMobject(
|
||||
"{\\partial {T} \\over \\partial {x}}(0, {t}) = ",
|
||||
"{\\partial {T} \\over \\partial {x}}(L, {t}) = ",
|
||||
"0",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
equation.next_to(bc_words, DOWN, MED_LARGE_BUFF)
|
||||
|
||||
self.play(
|
||||
self.camera_frame.shift, 0.8 * DOWN,
|
||||
)
|
||||
self.play(FadeInFrom(equation, UP))
|
||||
self.wait()
|
||||
|
||||
|
||||
class HeatEquationFrame(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
equation = self.get_d1_equation()
|
||||
equation.to_edge(UP, buff=MED_SMALL_BUFF)
|
||||
|
||||
ddx = equation[-11:]
|
||||
dt = equation[:11]
|
||||
|
||||
full_rect = FullScreenFadeRectangle(
|
||||
fill_color=DARK_GREY,
|
||||
fill_opacity=1,
|
||||
)
|
||||
smaller_rect = ScreenRectangle(
|
||||
height=6,
|
||||
fill_color=BLACK,
|
||||
fill_opacity=1,
|
||||
stroke_color=WHITE,
|
||||
stroke_width=2,
|
||||
)
|
||||
smaller_rect.next_to(equation, DOWN)
|
||||
|
||||
self.add(full_rect)
|
||||
self.add(smaller_rect)
|
||||
self.add(equation)
|
||||
self.wait()
|
||||
self.play(ShowCreationThenFadeAround(
|
||||
ddx,
|
||||
surrounding_rectangle_config={
|
||||
"stroke_color": GREEN,
|
||||
}
|
||||
))
|
||||
self.wait()
|
||||
self.play(ShowCreationThenFadeAround(dt))
|
||||
self.wait()
|
||||
|
||||
|
||||
class CompareFreqDecays1to2(Scene):
|
||||
CONFIG = {
|
||||
"freqs": [1, 2]
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
background = FullScreenFadeRectangle(
|
||||
fill_color=DARKER_GREY,
|
||||
fill_opacity=1,
|
||||
)
|
||||
|
||||
screens = VGroup(*[
|
||||
ScreenRectangle(
|
||||
height=4,
|
||||
fill_color=BLACK,
|
||||
fill_opacity=1,
|
||||
stroke_width=1,
|
||||
stroke_color=WHITE,
|
||||
)
|
||||
for x in range(2)
|
||||
])
|
||||
screens.arrange(RIGHT)
|
||||
screens.set_width(FRAME_WIDTH - 1)
|
||||
|
||||
formulas = VGroup(*[
|
||||
self.get_formula(freq)
|
||||
for freq in self.freqs
|
||||
])
|
||||
for formula, screen in zip(formulas, screens):
|
||||
formula.next_to(screen, UP)
|
||||
|
||||
self.add(background)
|
||||
self.add(screens)
|
||||
self.add(formulas)
|
||||
self.wait()
|
||||
|
||||
def get_formula(self, freq):
|
||||
f_str = str(freq)
|
||||
return TexMobject(
|
||||
"\\cos\\left(%s \\cdot {x}\\right)" % f_str,
|
||||
"e^{-\\alpha \\cdot %s^2 \\cdot {t}}" % f_str,
|
||||
tex_to_color_map={
|
||||
"{x}": GREEN,
|
||||
"{t}": YELLOW,
|
||||
f_str: MAROON_B,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class CompareFreqDecays1to4(CompareFreqDecays1to2):
|
||||
CONFIG = {
|
||||
"freqs": [1, 4],
|
||||
}
|
||||
|
||||
|
||||
class CompareFreqDecays2to4(CompareFreqDecays1to2):
|
||||
CONFIG = {
|
||||
"freqs": [2, 4],
|
||||
}
|
||||
|
||||
|
||||
class WorryAboutGenerality(TeacherStudentsScene, WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
eq = self.get_d1_equation()
|
||||
diffyq = self.get_diffyq_set()
|
||||
is_in = TexMobject("\\in")
|
||||
is_in.scale(2)
|
||||
|
||||
group = VGroup(eq, is_in, diffyq)
|
||||
group.arrange(RIGHT, buff=MED_LARGE_BUFF)
|
||||
group.to_edge(UP)
|
||||
|
||||
arrow = Vector(DOWN)
|
||||
arrow.set_stroke(WHITE, 5)
|
||||
arrow.next_to(eq, DOWN)
|
||||
themes = TextMobject("Frequent themes")
|
||||
themes.scale(1.5)
|
||||
themes.next_to(arrow, DOWN)
|
||||
|
||||
self.play(
|
||||
self.get_student_changes(
|
||||
"sad", "tired", "pleading"
|
||||
),
|
||||
self.teacher.change, "raise_right_hand",
|
||||
FadeInFromDown(eq)
|
||||
)
|
||||
self.play(Write(group[1:]))
|
||||
self.wait(2)
|
||||
self.play(
|
||||
ShowCreation(arrow),
|
||||
self.get_student_changes(*3 * ["pondering"]),
|
||||
)
|
||||
self.play(
|
||||
FadeInFrom(themes, UP),
|
||||
self.get_student_changes(*3 * ["thinking"]),
|
||||
self.teacher.change, "happy"
|
||||
)
|
||||
self.wait(4)
|
||||
|
||||
|
||||
# def get_d1_equation(self):
|
||||
# result = super().get_d1_equation()
|
||||
# lp, rp = parens = TexMobject("(", ")")
|
||||
# parens.match_height(result)
|
||||
# lp.next_to(result, LEFT, SMALL_BUFF)
|
||||
# rp.next_to(result, RIGHT, SMALL_BUFF)
|
||||
# result.add_to_back(lp)
|
||||
# result.add(rp)
|
||||
# return result
|
||||
|
||||
def get_diffyq_set(self):
|
||||
words = TextMobject(
|
||||
"Differential\\\\equations"
|
||||
)
|
||||
words.scale(1.5)
|
||||
words.set_color(BLUE)
|
||||
lb = Brace(words, LEFT)
|
||||
rb = Brace(words, RIGHT)
|
||||
return VGroup(lb, words, rb)
|
||||
712
from_3b1b/active/diffyq/part4/complex_functions.py
Normal file
712
from_3b1b/active/diffyq/part4/complex_functions.py
Normal file
@@ -0,0 +1,712 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class GeneralizeToComplexFunctions(Scene):
|
||||
CONFIG = {
|
||||
"axes_config": {
|
||||
"x_min": 0,
|
||||
"x_max": 10,
|
||||
"x_axis_config": {
|
||||
"stroke_width": 2,
|
||||
},
|
||||
"y_min": -2.5,
|
||||
"y_max": 2.5,
|
||||
"y_axis_config": {
|
||||
"tick_frequency": 0.25,
|
||||
"unit_size": 1.5,
|
||||
"include_tip": False,
|
||||
"stroke_width": 2,
|
||||
},
|
||||
},
|
||||
"complex_plane_config": {
|
||||
"axis_config": {
|
||||
"unit_size": 2
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.show_cosine_wave()
|
||||
self.transition_to_complex_plane()
|
||||
self.add_rotating_vectors_making_cos()
|
||||
|
||||
def show_cosine_wave(self):
|
||||
axes = Axes(**self.axes_config)
|
||||
axes.shift(2 * LEFT - axes.c2p(0, 0))
|
||||
y_axis = axes.y_axis
|
||||
y_labels = y_axis.get_number_mobjects(
|
||||
*range(-2, 3),
|
||||
number_config={"num_decimal_places": 1},
|
||||
)
|
||||
|
||||
t_tracker = ValueTracker(0)
|
||||
t_tracker.add_updater(lambda t, dt: t.increment_value(dt))
|
||||
get_t = t_tracker.get_value
|
||||
|
||||
def func(x):
|
||||
return 2 * np.cos(x)
|
||||
|
||||
cos_x_max = 20
|
||||
cos_wave = axes.get_graph(func, x_max=cos_x_max)
|
||||
cos_wave.set_color(YELLOW)
|
||||
shown_cos_wave = cos_wave.copy()
|
||||
shown_cos_wave.add_updater(
|
||||
lambda m: m.pointwise_become_partial(
|
||||
cos_wave, 0,
|
||||
np.clip(get_t() / cos_x_max, 0, 1),
|
||||
),
|
||||
)
|
||||
|
||||
dot = Dot()
|
||||
dot.set_color(PINK)
|
||||
dot.add_updater(lambda d: d.move_to(
|
||||
y_axis.n2p(func(get_t())),
|
||||
))
|
||||
|
||||
h_line = always_redraw(lambda: Line(
|
||||
dot.get_right(),
|
||||
shown_cos_wave.get_end(),
|
||||
stroke_width=1,
|
||||
))
|
||||
|
||||
real_words = TextMobject(
|
||||
"Real number\\\\output"
|
||||
)
|
||||
real_words.to_edge(LEFT)
|
||||
real_words.shift(2 * UP)
|
||||
real_arrow = Arrow()
|
||||
real_arrow.add_updater(
|
||||
lambda m: m.put_start_and_end_on(
|
||||
real_words.get_corner(DR),
|
||||
dot.get_center(),
|
||||
).scale(0.9),
|
||||
)
|
||||
|
||||
self.add(t_tracker)
|
||||
self.add(axes)
|
||||
self.add(y_labels)
|
||||
self.add(shown_cos_wave)
|
||||
self.add(dot)
|
||||
self.add(h_line)
|
||||
|
||||
self.wait(2)
|
||||
self.play(
|
||||
FadeInFrom(real_words, RIGHT),
|
||||
FadeIn(real_arrow),
|
||||
)
|
||||
self.wait(5)
|
||||
|
||||
y_axis.generate_target()
|
||||
y_axis.target.rotate(-90 * DEGREES)
|
||||
y_axis.target.center()
|
||||
y_axis.target.scale(2 / 1.5)
|
||||
y_labels.generate_target()
|
||||
for label in y_labels.target:
|
||||
label.next_to(
|
||||
y_axis.target.n2p(label.get_value()),
|
||||
DOWN, MED_SMALL_BUFF,
|
||||
)
|
||||
self.play(
|
||||
FadeOut(shown_cos_wave),
|
||||
FadeOut(axes.x_axis),
|
||||
FadeOut(h_line),
|
||||
)
|
||||
self.play(
|
||||
MoveToTarget(y_axis),
|
||||
MoveToTarget(y_labels),
|
||||
real_words.shift, 2 * RIGHT + UP,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.y_axis = y_axis
|
||||
self.y_labels = y_labels
|
||||
self.real_words = real_words
|
||||
self.real_arrow = real_arrow
|
||||
self.dot = dot
|
||||
self.t_tracker = t_tracker
|
||||
|
||||
def transition_to_complex_plane(self):
|
||||
y_axis = self.y_axis
|
||||
y_labels = self.y_labels
|
||||
|
||||
plane = self.get_complex_plane()
|
||||
plane_words = plane.label
|
||||
|
||||
self.add(plane, *self.get_mobjects())
|
||||
self.play(
|
||||
FadeOut(y_labels),
|
||||
FadeOut(y_axis),
|
||||
ShowCreation(plane),
|
||||
)
|
||||
self.play(Write(plane_words))
|
||||
self.wait()
|
||||
|
||||
self.plane = plane
|
||||
self.plane_words = plane_words
|
||||
|
||||
def add_rotating_vectors_making_cos(self):
|
||||
plane = self.plane
|
||||
real_words = self.real_words
|
||||
real_arrow = self.real_arrow
|
||||
t_tracker = self.t_tracker
|
||||
get_t = t_tracker.get_value
|
||||
|
||||
v1 = Vector(2 * RIGHT)
|
||||
v2 = Vector(2 * RIGHT)
|
||||
v1.set_color(BLUE)
|
||||
v2.set_color(interpolate_color(GREY_BROWN, WHITE, 0.5))
|
||||
v1.add_updater(
|
||||
lambda v: v.set_angle(get_t())
|
||||
)
|
||||
v2.add_updater(
|
||||
lambda v: v.set_angle(-get_t())
|
||||
)
|
||||
v1.add_updater(
|
||||
lambda v: v.shift(plane.n2p(0) - v.get_start())
|
||||
)
|
||||
# Change?
|
||||
v2.add_updater(
|
||||
lambda v: v.shift(plane.n2p(0) - v.get_start())
|
||||
)
|
||||
|
||||
ghost_v1 = v1.copy()
|
||||
ghost_v1.set_opacity(0.5)
|
||||
ghost_v1.add_updater(
|
||||
lambda v: v.shift(
|
||||
v2.get_end() - v.get_start()
|
||||
)
|
||||
)
|
||||
|
||||
ghost_v2 = v2.copy()
|
||||
ghost_v2.set_opacity(0.5)
|
||||
ghost_v2.add_updater(
|
||||
lambda v: v.shift(
|
||||
v1.get_end() - v.get_start()
|
||||
)
|
||||
)
|
||||
|
||||
circle = Circle(color=GREY_BROWN)
|
||||
circle.set_stroke(width=1)
|
||||
circle.set_width(2 * v1.get_length())
|
||||
circle.move_to(plane.n2p(0))
|
||||
|
||||
formula = TexMobject(
|
||||
# "\\cos(x) ="
|
||||
# "{1 \\over 2}e^{ix} +"
|
||||
# "{1 \\over 2}e^{-ix}",
|
||||
"2\\cos(x) =",
|
||||
"e^{ix}", "+", "e^{-ix}",
|
||||
tex_to_color_map={
|
||||
"e^{ix}": v1.get_color(),
|
||||
"e^{-ix}": v2.get_color(),
|
||||
}
|
||||
)
|
||||
formula.next_to(ORIGIN, UP, buff=0.75)
|
||||
# formula.add_background_rectangle()
|
||||
formula.set_stroke(BLACK, 3, background=True)
|
||||
formula.to_edge(LEFT, buff=MED_SMALL_BUFF)
|
||||
formula_brace = Brace(formula[1:], UP)
|
||||
formula_words = formula_brace.get_text(
|
||||
"Sum of\\\\rotations"
|
||||
)
|
||||
formula_words.set_stroke(BLACK, 3, background=True)
|
||||
|
||||
randy = Randolph()
|
||||
randy.to_corner(DL)
|
||||
randy.look_at(formula)
|
||||
|
||||
self.play(
|
||||
FadeOut(real_words),
|
||||
FadeOut(real_arrow),
|
||||
)
|
||||
self.play(
|
||||
FadeIn(v1),
|
||||
FadeIn(v2),
|
||||
FadeIn(circle),
|
||||
FadeIn(ghost_v1),
|
||||
FadeIn(ghost_v2),
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(FadeInFromDown(formula))
|
||||
self.play(
|
||||
GrowFromCenter(formula_brace),
|
||||
FadeIn(formula_words),
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(FadeIn(randy))
|
||||
self.play(randy.change, "pleading")
|
||||
self.play(Blink(randy))
|
||||
self.wait()
|
||||
self.play(randy.change, "confused")
|
||||
self.play(Blink(randy))
|
||||
self.wait()
|
||||
self.play(FadeOut(randy))
|
||||
self.wait(20)
|
||||
|
||||
#
|
||||
def get_complex_plane(self):
|
||||
plane = ComplexPlane(**self.complex_plane_config)
|
||||
plane.add_coordinates()
|
||||
|
||||
plane.label = TextMobject("Complex plane")
|
||||
plane.label.scale(1.5)
|
||||
plane.label.to_corner(UR, buff=MED_SMALL_BUFF)
|
||||
return plane
|
||||
|
||||
|
||||
class ClarifyInputAndOutput(GeneralizeToComplexFunctions):
|
||||
CONFIG = {
|
||||
"input_space_rect_config": {
|
||||
"stroke_color": WHITE,
|
||||
"stroke_width": 1,
|
||||
"fill_color": DARKER_GREY,
|
||||
"fill_opacity": 1,
|
||||
"width": 6,
|
||||
"height": 2,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.setup_plane()
|
||||
self.setup_input_space()
|
||||
self.setup_input_trackers()
|
||||
|
||||
self.describe_input()
|
||||
self.describe_output()
|
||||
|
||||
def setup_plane(self):
|
||||
plane = self.get_complex_plane()
|
||||
plane.sublabel = TextMobject("(Output space)")
|
||||
plane.sublabel.add_background_rectangle()
|
||||
plane.sublabel.next_to(plane.label, DOWN)
|
||||
self.add(plane, plane.label)
|
||||
self.plane = plane
|
||||
|
||||
def setup_input_space(self):
|
||||
rect = Rectangle(**self.input_space_rect_config)
|
||||
rect.to_corner(UL, buff=SMALL_BUFF)
|
||||
|
||||
input_line = self.get_input_line(rect)
|
||||
input_words = TextMobject("Input space")
|
||||
input_words.next_to(
|
||||
rect.get_bottom(), UP,
|
||||
SMALL_BUFF,
|
||||
)
|
||||
|
||||
self.add(rect)
|
||||
self.add(input_line)
|
||||
|
||||
self.input_rect = rect
|
||||
self.input_line = input_line
|
||||
self.input_words = input_words
|
||||
|
||||
def setup_input_trackers(self):
|
||||
plane = self.plane
|
||||
input_line = self.input_line
|
||||
input_tracker = ValueTracker(0)
|
||||
get_input = input_tracker.get_value
|
||||
|
||||
input_dot = Dot()
|
||||
input_dot.set_color(PINK)
|
||||
f_always(
|
||||
input_dot.move_to,
|
||||
lambda: input_line.n2p(get_input())
|
||||
)
|
||||
|
||||
input_decimal = DecimalNumber()
|
||||
input_decimal.scale(0.7)
|
||||
always(input_decimal.next_to, input_dot, UP)
|
||||
f_always(input_decimal.set_value, get_input)
|
||||
|
||||
path = self.get_path()
|
||||
|
||||
def get_output_point():
|
||||
return path.point_from_proportion(
|
||||
get_input()
|
||||
)
|
||||
|
||||
output_dot = Dot()
|
||||
output_dot.match_style(input_dot)
|
||||
f_always(output_dot.move_to, get_output_point)
|
||||
|
||||
output_vector = Vector()
|
||||
output_vector.set_color(WHITE)
|
||||
output_vector.add_updater(
|
||||
lambda v: v.put_start_and_end_on(
|
||||
plane.n2p(0),
|
||||
get_output_point()
|
||||
)
|
||||
)
|
||||
|
||||
output_decimal = DecimalNumber()
|
||||
output_decimal.scale(0.7)
|
||||
always(output_decimal.next_to, output_dot, UR, SMALL_BUFF)
|
||||
f_always(
|
||||
output_decimal.set_value,
|
||||
lambda: plane.p2n(get_output_point()),
|
||||
)
|
||||
|
||||
self.input_tracker = input_tracker
|
||||
self.input_dot = input_dot
|
||||
self.input_decimal = input_decimal
|
||||
self.path = path
|
||||
self.output_dot = output_dot
|
||||
self.output_vector = output_vector
|
||||
self.output_decimal = output_decimal
|
||||
|
||||
def describe_input(self):
|
||||
input_tracker = self.input_tracker
|
||||
|
||||
self.play(FadeInFrom(self.input_words, UP))
|
||||
self.play(
|
||||
FadeInFromLarge(self.input_dot),
|
||||
FadeIn(self.input_decimal),
|
||||
)
|
||||
for value in 1, 0:
|
||||
self.play(
|
||||
input_tracker.set_value, value,
|
||||
run_time=2
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def describe_output(self):
|
||||
path = self.path
|
||||
output_dot = self.output_dot
|
||||
output_decimal = self.output_decimal
|
||||
input_dot = self.input_dot
|
||||
input_tracker = self.input_tracker
|
||||
plane = self.plane
|
||||
real_line = plane.x_axis.copy()
|
||||
real_line.set_stroke(RED, 4)
|
||||
real_words = TextMobject("Real number line")
|
||||
real_words.next_to(ORIGIN, UP)
|
||||
real_words.to_edge(RIGHT)
|
||||
|
||||
traced_path = TracedPath(output_dot.get_center)
|
||||
traced_path.match_style(path)
|
||||
|
||||
self.play(
|
||||
ShowCreation(real_line),
|
||||
FadeInFrom(real_words, DOWN)
|
||||
)
|
||||
self.play(
|
||||
FadeOut(real_line),
|
||||
FadeOut(real_words),
|
||||
)
|
||||
self.play(
|
||||
FadeInFrom(plane.sublabel, UP)
|
||||
)
|
||||
self.play(
|
||||
FadeIn(output_decimal),
|
||||
TransformFromCopy(input_dot, output_dot),
|
||||
)
|
||||
|
||||
kw = {
|
||||
"run_time": 10,
|
||||
"rate_func": lambda t: smooth(t, 1),
|
||||
}
|
||||
self.play(
|
||||
ApplyMethod(input_tracker.set_value, 1, **kw),
|
||||
ShowCreation(path.copy(), remover=True, **kw),
|
||||
)
|
||||
self.add(path)
|
||||
self.add(output_dot)
|
||||
self.wait()
|
||||
|
||||
# Flatten to 1d
|
||||
real_function_word = TextMobject(
|
||||
"Real-valued function"
|
||||
)
|
||||
real_function_word.next_to(ORIGIN, DOWN, MED_LARGE_BUFF)
|
||||
path.generate_target()
|
||||
path.target.stretch(0, 1)
|
||||
path.target.move_to(plane.n2p(0))
|
||||
|
||||
self.play(
|
||||
FadeIn(real_function_word),
|
||||
MoveToTarget(path),
|
||||
)
|
||||
input_tracker.set_value(0)
|
||||
self.play(
|
||||
input_tracker.set_value, 1,
|
||||
**kw
|
||||
)
|
||||
|
||||
#
|
||||
def get_input_line(self, input_rect):
|
||||
input_line = UnitInterval()
|
||||
input_line.move_to(input_rect)
|
||||
input_line.shift(0.25 * UP)
|
||||
input_line.set_width(
|
||||
input_rect.get_width() - 1
|
||||
)
|
||||
input_line.add_numbers(0, 0.5, 1)
|
||||
return input_line
|
||||
|
||||
def get_path(self):
|
||||
# mob = SVGMobject("BatmanLogo")
|
||||
mob = TexMobject("\\pi")
|
||||
path = mob.family_members_with_points()[0]
|
||||
path.set_height(3.5)
|
||||
path.move_to(2 * DOWN, DOWN)
|
||||
path.set_stroke(YELLOW, 2)
|
||||
path.set_fill(opacity=0)
|
||||
return path
|
||||
|
||||
|
||||
class GraphForFlattenedPi(ClarifyInputAndOutput):
|
||||
CONFIG = {
|
||||
"camera_config": {"background_color": DARKER_GREY},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.setup_plane()
|
||||
plane = self.plane
|
||||
self.remove(plane, plane.label)
|
||||
|
||||
path = self.get_path()
|
||||
|
||||
axes = Axes(
|
||||
x_min=0,
|
||||
x_max=1,
|
||||
x_axis_config={
|
||||
"unit_size": 7,
|
||||
"include_tip": False,
|
||||
"tick_frequency": 0.1,
|
||||
},
|
||||
y_min=-1.5,
|
||||
y_max=1.5,
|
||||
y_axis_config={
|
||||
"include_tip": False,
|
||||
"unit_size": 2.5,
|
||||
"tick_frequency": 0.5,
|
||||
},
|
||||
)
|
||||
axes.set_width(FRAME_WIDTH - 1)
|
||||
axes.set_height(FRAME_HEIGHT - 1, stretch=True)
|
||||
axes.center()
|
||||
|
||||
axes.x_axis.add_numbers(
|
||||
0.5, 1.0,
|
||||
number_config={"num_decimal_places": 1},
|
||||
)
|
||||
axes.y_axis.add_numbers(
|
||||
-1.0, 1.0,
|
||||
number_config={"num_decimal_places": 1},
|
||||
)
|
||||
|
||||
def func(t):
|
||||
return plane.x_axis.p2n(
|
||||
path.point_from_proportion(t)
|
||||
)
|
||||
|
||||
graph = axes.get_graph(func)
|
||||
graph.set_color(PINK)
|
||||
|
||||
v_line = always_redraw(lambda: Line(
|
||||
axes.x_axis.n2p(axes.x_axis.p2n(graph.get_end())),
|
||||
graph.get_end(),
|
||||
stroke_width=1,
|
||||
))
|
||||
|
||||
self.add(axes)
|
||||
self.add(v_line)
|
||||
|
||||
kw = {
|
||||
"run_time": 10,
|
||||
"rate_func": lambda t: smooth(t, 1),
|
||||
}
|
||||
self.play(ShowCreation(graph, **kw))
|
||||
self.wait()
|
||||
|
||||
|
||||
class SimpleComplexExponentExample(ClarifyInputAndOutput):
|
||||
CONFIG = {
|
||||
"input_space_rect_config": {
|
||||
"width": 14,
|
||||
"height": 1.5,
|
||||
},
|
||||
"input_line_config": {
|
||||
"unit_size": 0.5,
|
||||
"x_min": 0,
|
||||
"x_max": 25,
|
||||
"stroke_width": 2,
|
||||
},
|
||||
"input_numbers": range(0, 30, 5),
|
||||
"input_tex_args": ["t", "="],
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.setup_plane()
|
||||
self.setup_input_space()
|
||||
self.setup_input_trackers()
|
||||
self.setup_output_trackers()
|
||||
|
||||
# Testing
|
||||
time = self.input_line.x_max
|
||||
self.play(
|
||||
self.input_tracker.set_value, time,
|
||||
run_time=time,
|
||||
rate_func=linear,
|
||||
)
|
||||
|
||||
def setup_plane(self):
|
||||
plane = ComplexPlane()
|
||||
plane.scale(2)
|
||||
plane.add_coordinates()
|
||||
plane.shift(DOWN)
|
||||
self.plane = plane
|
||||
self.add(plane)
|
||||
|
||||
def setup_input_trackers(self):
|
||||
input_line = self.input_line
|
||||
input_tracker = ValueTracker(0)
|
||||
get_input = input_tracker.get_value
|
||||
|
||||
input_tip = ArrowTip(start_angle=-TAU / 4)
|
||||
input_tip.scale(0.5)
|
||||
input_tip.set_color(PINK)
|
||||
f_always(
|
||||
input_tip.move_to,
|
||||
lambda: input_line.n2p(get_input()),
|
||||
lambda: DOWN,
|
||||
)
|
||||
|
||||
input_label = VGroup(
|
||||
TexMobject(*self.input_tex_args),
|
||||
DecimalNumber(),
|
||||
)
|
||||
input_label[0].set_color_by_tex("t", PINK)
|
||||
input_label.scale(0.7)
|
||||
input_label.add_updater(
|
||||
lambda m: m.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
)
|
||||
input_label.add_updater(
|
||||
lambda m: m[1].set_value(get_input())
|
||||
)
|
||||
input_label.add_updater(
|
||||
lambda m: m.next_to(input_tip, UP, SMALL_BUFF)
|
||||
)
|
||||
|
||||
self.input_tracker = input_tracker
|
||||
self.input_tip = input_tip
|
||||
self.input_label = input_label
|
||||
|
||||
self.add(input_tip, input_label)
|
||||
|
||||
def setup_output_trackers(self):
|
||||
plane = self.plane
|
||||
get_input = self.input_tracker.get_value
|
||||
|
||||
def get_output():
|
||||
return np.exp(complex(0, get_input()))
|
||||
|
||||
def get_output_point():
|
||||
return plane.n2p(get_output())
|
||||
|
||||
output_label, static_output_label = [
|
||||
TexMobject(
|
||||
"e^{i t}" + s,
|
||||
tex_to_color_map={"t": PINK},
|
||||
background_stroke_width=3,
|
||||
)
|
||||
for s in ["", "\\approx"]
|
||||
]
|
||||
output_label.scale(1.2)
|
||||
output_label.add_updater(
|
||||
lambda m: m.shift(
|
||||
-m.get_bottom() +
|
||||
get_output_point() +
|
||||
rotate_vector(
|
||||
0.35 * RIGHT,
|
||||
get_input(),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
output_vector = Vector()
|
||||
output_vector.set_opacity(0.75)
|
||||
output_vector.add_updater(
|
||||
lambda m: m.put_start_and_end_on(
|
||||
plane.n2p(0), get_output_point(),
|
||||
)
|
||||
)
|
||||
|
||||
t_max = 40
|
||||
full_output_path = ParametricFunction(
|
||||
lambda t: plane.n2p(np.exp(complex(0, t))),
|
||||
t_min=0,
|
||||
t_max=t_max
|
||||
)
|
||||
output_path = VMobject()
|
||||
output_path.set_stroke(YELLOW, 2)
|
||||
output_path.add_updater(
|
||||
lambda m: m.pointwise_become_partial(
|
||||
full_output_path,
|
||||
0, get_input() / t_max,
|
||||
)
|
||||
)
|
||||
|
||||
static_output_label.next_to(plane.c2p(1, 1), UR)
|
||||
output_decimal = DecimalNumber(
|
||||
include_sign=True,
|
||||
)
|
||||
output_decimal.scale(0.8)
|
||||
output_decimal.set_stroke(BLACK, 3, background=True)
|
||||
output_decimal.add_updater(
|
||||
lambda m: m.set_value(get_output())
|
||||
)
|
||||
output_decimal.add_updater(
|
||||
lambda m: m.next_to(
|
||||
static_output_label,
|
||||
RIGHT, 2 * SMALL_BUFF,
|
||||
aligned_edge=DOWN,
|
||||
)
|
||||
)
|
||||
|
||||
self.add(output_path)
|
||||
self.add(output_vector)
|
||||
self.add(output_label)
|
||||
self.add(static_output_label)
|
||||
self.add(BackgroundRectangle(output_decimal))
|
||||
self.add(output_decimal)
|
||||
|
||||
#
|
||||
def get_input_line(self, input_rect):
|
||||
input_line = NumberLine(**self.input_line_config)
|
||||
input_line.move_to(input_rect)
|
||||
input_line.set_width(
|
||||
input_rect.get_width() - 1.5,
|
||||
stretch=True,
|
||||
)
|
||||
input_line.add_numbers(*self.input_numbers)
|
||||
return input_line
|
||||
|
||||
|
||||
class TRangingFrom0To1(SimpleComplexExponentExample):
|
||||
CONFIG = {
|
||||
"input_space_rect_config": {
|
||||
"width": 6,
|
||||
"height": 2,
|
||||
},
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.setup_input_space()
|
||||
self.setup_input_trackers()
|
||||
|
||||
self.play(
|
||||
self.input_tracker.set_value, 1,
|
||||
run_time=10,
|
||||
rate_func=linear
|
||||
)
|
||||
|
||||
def get_input_line(self, rect):
|
||||
result = ClarifyInputAndOutput.get_input_line(self, rect)
|
||||
result.stretch(0.9, 0)
|
||||
result.set_stroke(width=2)
|
||||
for sm in result.get_family():
|
||||
if isinstance(sm, DecimalNumber):
|
||||
sm.stretch(1 / 0.9, 0)
|
||||
sm.set_stroke(width=0)
|
||||
return result
|
||||
1893
from_3b1b/active/diffyq/part4/fourier_series_scenes.py
Normal file
1893
from_3b1b/active/diffyq/part4/fourier_series_scenes.py
Normal file
File diff suppressed because it is too large
Load Diff
242
from_3b1b/active/diffyq/part4/long_fourier_scenes.py
Normal file
242
from_3b1b/active/diffyq/part4/long_fourier_scenes.py
Normal file
@@ -0,0 +1,242 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
from active_projects.diffyq.part4.fourier_series_scenes import ComplexFourierSeriesExample
|
||||
from manimlib.once_useful_constructs.fractals import HilbertCurve
|
||||
|
||||
|
||||
class FourierSeriesExampleWithRectForZoom(ComplexFourierSeriesExample):
|
||||
CONFIG = {
|
||||
"n_vectors": 100,
|
||||
"slow_factor": 0.01,
|
||||
"rect_scale_factor": 0.1,
|
||||
"start_drawn": True,
|
||||
"drawing_height": 7,
|
||||
"rect_stroke_width": 1,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_vectors_circles_path()
|
||||
self.circles.set_stroke(opacity=0.5)
|
||||
rect = self.rect = self.get_rect()
|
||||
rect.set_height(self.rect_scale_factor * FRAME_HEIGHT)
|
||||
rect.add_updater(lambda m: m.move_to(
|
||||
self.get_rect_center()
|
||||
))
|
||||
self.add(rect)
|
||||
self.run_one_cycle()
|
||||
|
||||
def get_rect_center(self):
|
||||
return center_of_mass([
|
||||
v.get_end()
|
||||
for v in self.vectors
|
||||
])
|
||||
|
||||
def get_rect(self):
|
||||
return ScreenRectangle(
|
||||
color=BLUE,
|
||||
stroke_width=self.rect_stroke_width,
|
||||
)
|
||||
|
||||
|
||||
class ZoomedInFourierSeriesExample(FourierSeriesExampleWithRectForZoom, MovingCameraScene):
|
||||
CONFIG = {
|
||||
"vector_config": {
|
||||
"max_tip_length_to_length_ratio": 0.15,
|
||||
"tip_length": 0.05,
|
||||
},
|
||||
"parametric_function_step_size": 0.001,
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
ComplexFourierSeriesExample.setup(self)
|
||||
MovingCameraScene.setup(self)
|
||||
|
||||
def get_rect(self):
|
||||
return self.camera_frame
|
||||
|
||||
def add_vectors_circles_path(self):
|
||||
super().add_vectors_circles_path()
|
||||
for v in self.vectors:
|
||||
if v.get_stroke_width() < 1:
|
||||
v.set_stroke(width=1)
|
||||
|
||||
|
||||
class ZoomedInFourierSeriesExample100x(ZoomedInFourierSeriesExample):
|
||||
CONFIG = {
|
||||
"vector_config": {
|
||||
"max_tip_length_to_length_ratio": 0.15 * 0.4,
|
||||
"tip_length": 0.05 * 0.2,
|
||||
"max_stroke_width_to_length_ratio": 80,
|
||||
"stroke_width": 3,
|
||||
},
|
||||
"max_circle_stroke_width": 0.5,
|
||||
"rect_scale_factor": 0.01,
|
||||
# "parametric_function_step_size": 0.01,
|
||||
}
|
||||
|
||||
def get_rect_center(self):
|
||||
return self.vectors[-1].get_end()
|
||||
|
||||
# def get_drawn_path(self, vectors, stroke_width=2, **kwargs):
|
||||
# return self.get_path_end(vectors, stroke_width, **kwargs)
|
||||
|
||||
|
||||
class TrebleClefFourierSeriesExampleWithRectForZoom(FourierSeriesExampleWithRectForZoom):
|
||||
CONFIG = {
|
||||
"file_name": "TrebleClef",
|
||||
"drawn_path_stroke_width": 10,
|
||||
}
|
||||
|
||||
|
||||
class TrebleClefZoomedInFourierSeriesExample(ZoomedInFourierSeriesExample):
|
||||
CONFIG = {
|
||||
"file_name": "TrebleClef",
|
||||
}
|
||||
|
||||
|
||||
class NailAndGearFourierSeriesExampleWithRectForZoom(FourierSeriesExampleWithRectForZoom):
|
||||
CONFIG = {
|
||||
"file_name": "Nail_And_Gear",
|
||||
"n_vectors": 200,
|
||||
"drawn_path_color": "#39FF14",
|
||||
}
|
||||
|
||||
|
||||
class NailAndGearZoomedInFourierSeriesExample(ZoomedInFourierSeriesExample):
|
||||
CONFIG = {
|
||||
"file_name": "Nail_And_Gear",
|
||||
"n_vectors": 200,
|
||||
"drawn_path_color": "#39FF14",
|
||||
}
|
||||
|
||||
|
||||
class SigmaFourierSeriesExampleWithRectForZoom(FourierSeriesExampleWithRectForZoom):
|
||||
CONFIG = {
|
||||
"n_vectors": 200,
|
||||
"drawn_path_color": PINK,
|
||||
"rect_stroke_width": 0,
|
||||
}
|
||||
|
||||
def get_shape(self):
|
||||
return TexMobject("\\Sigma")
|
||||
|
||||
|
||||
class SigmaZoomedInFourierSeriesExample(SigmaFourierSeriesExampleWithRectForZoom, ZoomedInFourierSeriesExample):
|
||||
pass
|
||||
|
||||
|
||||
class FourierOfFourier(FourierSeriesExampleWithRectForZoom):
|
||||
CONFIG = {
|
||||
"file_name": "FourierOneLine",
|
||||
"n_vectors": 300,
|
||||
"rect_stroke_width": 1,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfFourierZoomedIn(ZoomedInFourierSeriesExample):
|
||||
CONFIG = {
|
||||
"file_name": "FourierOneLine",
|
||||
"max_circle_stroke_width": 0.3,
|
||||
"n_vectors": 300,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfFourier100xZoom(ZoomedInFourierSeriesExample100x):
|
||||
CONFIG = {
|
||||
"file_name": "FourierOneLine",
|
||||
"max_circle_stroke_width": 0.3,
|
||||
"n_vectors": 300,
|
||||
"slow_factor": 0.001,
|
||||
}
|
||||
|
||||
def run_one_cycle(self):
|
||||
self.vector_clock.set_value(0.3)
|
||||
self.wait(40)
|
||||
|
||||
|
||||
class FourierOfHilbert(FourierSeriesExampleWithRectForZoom):
|
||||
CONFIG = {
|
||||
"n_vectors": 300,
|
||||
"rect_stroke_width": 1,
|
||||
"drawn_path_stroke_width": 4,
|
||||
"drawn_path_color": BLUE,
|
||||
}
|
||||
|
||||
def get_path(self):
|
||||
path = HilbertCurve(order=5)
|
||||
path.set_height(self.drawing_height)
|
||||
path.to_edge(DOWN)
|
||||
combined_path = VMobject()
|
||||
for sm in path.family_members_with_points():
|
||||
combined_path.append_vectorized_mobject(sm)
|
||||
start = combined_path.get_start()
|
||||
end = combined_path.get_end()
|
||||
points = [
|
||||
interpolate(end, start, alpha)
|
||||
for alpha in np.linspace(0, 1, 10)
|
||||
]
|
||||
for point in points:
|
||||
combined_path.add_line_to(point)
|
||||
|
||||
combined_path.set_stroke(width=0)
|
||||
return combined_path
|
||||
|
||||
|
||||
class FourierOfHilbertZoomedIn(FourierOfHilbert, ZoomedInFourierSeriesExample):
|
||||
pass
|
||||
|
||||
|
||||
class FourierOfBritain(FourierSeriesExampleWithRectForZoom):
|
||||
CONFIG = {
|
||||
"file_name": "Britain",
|
||||
"n_vectors": 500,
|
||||
"drawn_path_color": RED,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfBritainZoomedIn(FourierOfBritain, ZoomedInFourierSeriesExample):
|
||||
pass
|
||||
|
||||
|
||||
class FourierOfSeattle(FourierSeriesExampleWithRectForZoom):
|
||||
CONFIG = {
|
||||
"file_name": "SeattleSkyline",
|
||||
"drawing_height": 7,
|
||||
"n_vectors": 400,
|
||||
"drawn_path_color": TEAL,
|
||||
"drawn_path_stroke_width": 5,
|
||||
}
|
||||
|
||||
|
||||
class FourierOfSeattleZoomedIn(ZoomedInFourierSeriesExample):
|
||||
CONFIG = {
|
||||
"file_name": "SeattleSkyline",
|
||||
"drawing_height": 7,
|
||||
"n_vectors": 400,
|
||||
"drawn_path_color": TEAL,
|
||||
"drawn_path_stroke_width": 5,
|
||||
"max_circle_stroke_width": 0.3,
|
||||
}
|
||||
|
||||
|
||||
class VideoWrapper(Scene):
|
||||
def construct(self):
|
||||
fade_rect = FullScreenFadeRectangle()
|
||||
fade_rect.set_fill(DARK_GREY, 1)
|
||||
screen_rect = ScreenRectangle()
|
||||
screen_rect.set_height(4)
|
||||
screen_rect.set_fill(BLACK, 1)
|
||||
screen_rect.set_stroke(width=0)
|
||||
|
||||
boundary = AnimatedBoundary(screen_rect)
|
||||
|
||||
title = TextMobject("Learn the math")
|
||||
title.scale(1.5)
|
||||
title.next_to(screen_rect, UP)
|
||||
|
||||
self.add(fade_rect)
|
||||
self.add(screen_rect)
|
||||
self.add(boundary)
|
||||
|
||||
self.play(FadeInFromDown(title))
|
||||
self.wait(19)
|
||||
235
from_3b1b/active/diffyq/part4/pi_creature_scenes.py
Normal file
235
from_3b1b/active/diffyq/part4/pi_creature_scenes.py
Normal file
@@ -0,0 +1,235 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
|
||||
class WhyWouldYouCare(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.student_says(
|
||||
"Who cares!",
|
||||
target_mode="sassy",
|
||||
student_index=2,
|
||||
added_anims=[self.teacher.change, "guilty"],
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
RemovePiCreatureBubble(self.students[2]),
|
||||
self.teacher.change, "raise_right_hand",
|
||||
self.get_student_changes(
|
||||
"pondering", "erm", "thinking",
|
||||
look_at_arg=self.screen,
|
||||
)
|
||||
)
|
||||
self.look_at(self.screen)
|
||||
self.wait(5)
|
||||
|
||||
|
||||
class SolveForWavesNothingElse(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.student_says(
|
||||
"Sure, we can\\\\solve it for\\\\sums of waves...",
|
||||
target_mode="sassy",
|
||||
student_index=2,
|
||||
added_anims=[self.teacher.change, "guilty"]
|
||||
)
|
||||
self.change_student_modes("pondering", "pondering", "sassy")
|
||||
self.look_at(self.screen)
|
||||
self.wait(4)
|
||||
self.student_says(
|
||||
"But nothing else!",
|
||||
target_mode="angry",
|
||||
)
|
||||
self.change_student_modes(
|
||||
"concerned_musician",
|
||||
"concerned_musician",
|
||||
"angry",
|
||||
)
|
||||
self.wait(5)
|
||||
|
||||
|
||||
class HangOnThere(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
student = self.students[2]
|
||||
|
||||
axes1 = Axes(
|
||||
x_min=0,
|
||||
x_max=1,
|
||||
y_min=-1.5,
|
||||
y_max=1.5,
|
||||
x_axis_config={
|
||||
"tick_frequency": 0.25,
|
||||
"include_tip": False,
|
||||
"unit_size": 3,
|
||||
},
|
||||
y_axis_config={
|
||||
"tick_frequency": 0.5,
|
||||
"include_tip": False,
|
||||
},
|
||||
)
|
||||
axes1.set_stroke(width=2)
|
||||
axes2 = axes1.deepcopy()
|
||||
neq = TexMobject("\\neq")
|
||||
neq.scale(2)
|
||||
|
||||
group = VGroup(axes1, neq, axes2)
|
||||
group.arrange(RIGHT)
|
||||
group.set_height(4)
|
||||
group.next_to(
|
||||
student.get_corner(UL), UP,
|
||||
buff=LARGE_BUFF,
|
||||
)
|
||||
|
||||
step_graph = axes1.get_graph(
|
||||
lambda x: (1 if x < 0.5 else -1),
|
||||
discontinuities=[0.5],
|
||||
)
|
||||
step_graph.set_color(YELLOW)
|
||||
wave_graphs = VGroup(*[
|
||||
axes2.get_graph(
|
||||
lambda x: (4 / PI) * np.sum([
|
||||
(u / n) * np.cos(n * PI * x)
|
||||
for u, n in zip(
|
||||
it.cycle([1, -1]),
|
||||
range(1, max_n, 2),
|
||||
)
|
||||
]),
|
||||
)
|
||||
for max_n in range(3, 103, 2)
|
||||
])
|
||||
wave_graphs.set_stroke(width=3)
|
||||
wave_graphs.set_color_by_gradient(WHITE, PINK)
|
||||
last_wave_graph = wave_graphs[-1]
|
||||
last_wave_graph.set_stroke(PINK, 2)
|
||||
wave_graphs.remove(last_wave_graph)
|
||||
# wave_graphs[-1].set_stroke(width=3)
|
||||
# wave_graphs[-1].set_stroke(BLACK, 5, background=True)
|
||||
group.add(step_graph)
|
||||
|
||||
self.student_says(
|
||||
"Hang on\\\\hang on\\\\hang on...",
|
||||
target_mode="surprised",
|
||||
content_introduction_class=FadeIn,
|
||||
student_index=2,
|
||||
added_anims=[
|
||||
self.teacher.change, "guilty"
|
||||
],
|
||||
run_time=1,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
RemovePiCreatureBubble(
|
||||
student,
|
||||
target_mode="raise_left_hand",
|
||||
look_at_arg=group,
|
||||
),
|
||||
FadeInFromDown(group),
|
||||
)
|
||||
|
||||
last_wg = VectorizedPoint()
|
||||
n_first_fades = 4
|
||||
for wg in wave_graphs[:n_first_fades]:
|
||||
self.play(
|
||||
last_wg.set_stroke, {"width": 0.1},
|
||||
FadeIn(wg),
|
||||
)
|
||||
last_wg = wg
|
||||
self.play(
|
||||
LaggedStart(
|
||||
*[
|
||||
UpdateFromAlphaFunc(
|
||||
wg,
|
||||
lambda m, a: m.set_stroke(
|
||||
width=(3 * there_and_back(a) + 0.1 * a)
|
||||
),
|
||||
)
|
||||
for wg in wave_graphs[n_first_fades:]
|
||||
],
|
||||
run_time=5,
|
||||
lag_ratio=0.2,
|
||||
),
|
||||
ApplyMethod(
|
||||
last_wg.set_stroke, {"width": 0.1},
|
||||
run_time=0.25,
|
||||
),
|
||||
FadeIn(
|
||||
last_wave_graph,
|
||||
rate_func=squish_rate_func(smooth, 0.9, 1),
|
||||
run_time=5,
|
||||
),
|
||||
self.teacher.change, "thinking",
|
||||
)
|
||||
self.change_student_modes(
|
||||
"confused", "confused", "angry"
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class YouSaidThisWasEasier(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.change_all_student_modes(
|
||||
"confused", look_at_arg=self.screen,
|
||||
)
|
||||
self.student_says(
|
||||
"I'm sorry, you said\\\\this was easier?",
|
||||
target_mode="sassy"
|
||||
)
|
||||
self.play(self.teacher.change, "guilty")
|
||||
self.wait(3)
|
||||
self.teacher_says(
|
||||
"Bear with\\\\me",
|
||||
bubble_kwargs={"height": 3, "width": 3},
|
||||
)
|
||||
self.look_at(self.screen)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class LooseWithLanguage(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
terms = VGroup(
|
||||
TextMobject("``Complex number''"),
|
||||
TextMobject("``Vector''"),
|
||||
)
|
||||
colors = [YELLOW, BLUE]
|
||||
for term, color in zip(terms, colors):
|
||||
term.set_color(color)
|
||||
|
||||
terms.scale(1.5)
|
||||
terms.arrange(DOWN, buff=LARGE_BUFF)
|
||||
terms.to_edge(UP)
|
||||
terms.match_x(self.students)
|
||||
|
||||
self.teacher_says(
|
||||
"Loose with\\\\language",
|
||||
bubble_kwargs={"width": 3, "height": 3},
|
||||
run_time=2,
|
||||
)
|
||||
self.play(
|
||||
FadeInFrom(terms[1], DOWN),
|
||||
self.get_student_changes(
|
||||
"thinking", "pondering", "erm",
|
||||
look_at_arg=terms,
|
||||
)
|
||||
)
|
||||
self.play(FadeInFromDown(terms[0]))
|
||||
self.wait()
|
||||
self.play(Swap(*terms))
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class FormulaOutOfContext(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
formula = TexMobject(
|
||||
"c_{n} = \\int_0^1 e^{-2\\pi i {n} {t}}f({t}){dt}",
|
||||
tex_to_color_map={
|
||||
"{n}": YELLOW,
|
||||
"{t}": PINK,
|
||||
}
|
||||
)
|
||||
formula.scale(1.5)
|
||||
formula.next_to(self.students, UP, LARGE_BUFF)
|
||||
|
||||
self.add(formula)
|
||||
self.change_all_student_modes(
|
||||
"horrified",
|
||||
look_at_arg=formula,
|
||||
)
|
||||
self.play(self.teacher.change, "tease")
|
||||
self.wait(3)
|
||||
2471
from_3b1b/active/diffyq/part4/staging.py
Normal file
2471
from_3b1b/active/diffyq/part4/staging.py
Normal file
File diff suppressed because it is too large
Load Diff
371
from_3b1b/active/diffyq/part4/temperature_scenes.py
Normal file
371
from_3b1b/active/diffyq/part4/temperature_scenes.py
Normal file
@@ -0,0 +1,371 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.diffyq.part2.heat_equation import BringTwoRodsTogether
|
||||
from active_projects.diffyq.part3.staging import FourierSeriesIllustraiton
|
||||
|
||||
|
||||
class StepFunctionExample(BringTwoRodsTogether, FourierSeriesIllustraiton):
|
||||
CONFIG = {
|
||||
"axes_config": {
|
||||
"y_min": -1.5,
|
||||
"y_max": 1.5,
|
||||
"y_axis_config": {
|
||||
"unit_size": 2.5,
|
||||
"tick_frequency": 0.5,
|
||||
},
|
||||
"x_min": 0,
|
||||
"x_max": 1,
|
||||
"x_axis_config": {
|
||||
"unit_size": 8,
|
||||
"tick_frequency": 0.1,
|
||||
"include_tip": False,
|
||||
},
|
||||
},
|
||||
"y_labels": [-1, 1],
|
||||
"graph_x_min": 0,
|
||||
"graph_x_max": 1,
|
||||
"midpoint": 0.5,
|
||||
"min_temp": -1,
|
||||
"max_temp": 1,
|
||||
"alpha": 0.25,
|
||||
"step_size": 0.01,
|
||||
"n_range": range(1, 41, 2),
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.setup_axes()
|
||||
self.setup_graph()
|
||||
self.setup_clock()
|
||||
|
||||
self.bring_rods_together()
|
||||
self.let_evolve_for_a_bit()
|
||||
self.add_labels()
|
||||
self.compare_to_sine_wave()
|
||||
self.sum_of_sine_waves()
|
||||
|
||||
def bring_rods_together(self):
|
||||
rods = VGroup(
|
||||
self.get_rod(0, 0.5),
|
||||
self.get_rod(0.5, 1),
|
||||
)
|
||||
rods.add_updater(self.update_rods)
|
||||
|
||||
arrows = VGroup(
|
||||
Vector(RIGHT).next_to(rods[0], UP),
|
||||
Vector(LEFT).next_to(rods[1], UP),
|
||||
)
|
||||
|
||||
words = VGroup(
|
||||
TextMobject("Hot").next_to(rods[0], DOWN),
|
||||
TextMobject("Cold").next_to(rods[1], DOWN),
|
||||
)
|
||||
|
||||
for pair in rods, words:
|
||||
pair.save_state()
|
||||
pair.space_out_submobjects(1.2)
|
||||
|
||||
black_rects = VGroup(*[
|
||||
Square(
|
||||
side_length=1,
|
||||
fill_color=BLACK,
|
||||
fill_opacity=1,
|
||||
stroke_width=0,
|
||||
).move_to(self.axes.c2p(0, u))
|
||||
for u in [1, -1]
|
||||
])
|
||||
black_rects[0].add_updater(
|
||||
lambda m: m.align_to(rods[0].get_right(), LEFT)
|
||||
)
|
||||
black_rects[1].add_updater(
|
||||
lambda m: m.align_to(rods[1].get_left(), RIGHT)
|
||||
)
|
||||
|
||||
self.add(
|
||||
self.axes,
|
||||
self.graph,
|
||||
self.clock,
|
||||
)
|
||||
self.add(rods, words)
|
||||
self.add(black_rects)
|
||||
|
||||
kw = {
|
||||
"run_time": 2,
|
||||
"rate_func": rush_into,
|
||||
}
|
||||
self.play(
|
||||
Restore(rods, **kw),
|
||||
Restore(words, **kw),
|
||||
*map(ShowCreation, arrows)
|
||||
)
|
||||
self.remove(black_rects)
|
||||
|
||||
self.to_fade = VGroup(words, arrows)
|
||||
self.rods = rods
|
||||
|
||||
def let_evolve_for_a_bit(self):
|
||||
rods = self.rods
|
||||
# axes = self.axes
|
||||
time_label = self.time_label
|
||||
graph = self.graph
|
||||
graph.save_state()
|
||||
|
||||
graph.add_updater(self.update_graph)
|
||||
time_label.next_to(self.clock, DOWN)
|
||||
time_label.add_updater(
|
||||
lambda d, dt: d.increment_value(dt)
|
||||
)
|
||||
rods.add_updater(self.update_rods)
|
||||
|
||||
self.add(time_label)
|
||||
self.play(
|
||||
FadeOut(self.to_fade),
|
||||
self.get_clock_anim(1)
|
||||
)
|
||||
self.play(self.get_clock_anim(3))
|
||||
|
||||
time_label.clear_updaters()
|
||||
graph.clear_updaters()
|
||||
self.play(
|
||||
self.get_clock_anim(
|
||||
-4,
|
||||
run_time=1,
|
||||
rate_func=smooth,
|
||||
),
|
||||
graph.restore,
|
||||
time_label.set_value, 0,
|
||||
)
|
||||
rods.clear_updaters()
|
||||
self.wait()
|
||||
|
||||
def add_labels(self):
|
||||
axes = self.axes
|
||||
y_axis = axes.y_axis
|
||||
x_axis = axes.x_axis
|
||||
y_numbers = y_axis.get_number_mobjects(
|
||||
*np.arange(-1, 1.5, 0.5),
|
||||
number_config={
|
||||
"unit": "^\\circ",
|
||||
"num_decimal_places": 1,
|
||||
}
|
||||
)
|
||||
x_numbers = x_axis.get_number_mobjects(
|
||||
*np.arange(0.2, 1.2, 0.2),
|
||||
number_config={
|
||||
"num_decimal_places": 1,
|
||||
},
|
||||
)
|
||||
|
||||
self.play(FadeIn(y_numbers))
|
||||
self.play(ShowCreationThenFadeAround(y_numbers[-1]))
|
||||
self.play(ShowCreationThenFadeAround(y_numbers[0]))
|
||||
self.play(
|
||||
LaggedStartMap(
|
||||
FadeInFrom, x_numbers,
|
||||
lambda m: (m, UP)
|
||||
),
|
||||
self.rods.set_opacity, 0.8,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def compare_to_sine_wave(self):
|
||||
phi_tracker = ValueTracker(0)
|
||||
get_phi = phi_tracker.get_value
|
||||
k_tracker = ValueTracker(TAU)
|
||||
get_k = k_tracker.get_value
|
||||
A_tracker = ValueTracker(1)
|
||||
get_A = A_tracker.get_value
|
||||
|
||||
sine_wave = always_redraw(lambda: self.axes.get_graph(
|
||||
lambda x: get_A() * np.sin(
|
||||
get_k() * x - get_phi()
|
||||
),
|
||||
x_min=self.graph_x_min,
|
||||
x_max=self.graph_x_max,
|
||||
).color_using_background_image("VerticalTempGradient"))
|
||||
|
||||
self.play(ShowCreation(sine_wave, run_time=3))
|
||||
self.wait()
|
||||
self.play(A_tracker.set_value, 1.25)
|
||||
self.play(A_tracker.set_value, 0.75)
|
||||
self.play(phi_tracker.set_value, -PI / 2)
|
||||
self.play(k_tracker.set_value, 3 * TAU)
|
||||
self.play(k_tracker.set_value, 2 * TAU)
|
||||
self.play(
|
||||
k_tracker.set_value, PI,
|
||||
A_tracker.set_value, 4 / PI,
|
||||
run_time=3
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.sine_wave = sine_wave
|
||||
|
||||
def sum_of_sine_waves(self):
|
||||
curr_sine_wave = self.sine_wave
|
||||
axes = self.axes
|
||||
|
||||
sine_graphs = self.get_sine_graphs(axes)
|
||||
partial_sums = self.get_partial_sums(axes, sine_graphs)
|
||||
|
||||
curr_partial_sum = partial_sums[0]
|
||||
curr_partial_sum.set_color(WHITE)
|
||||
self.play(
|
||||
FadeOut(curr_sine_wave),
|
||||
FadeIn(curr_partial_sum),
|
||||
FadeOut(self.rods),
|
||||
)
|
||||
# Copy-pasting from superclass...in theory,
|
||||
# this should be better abstracted, but eh.
|
||||
pairs = list(zip(sine_graphs, partial_sums))[1:]
|
||||
for sine_graph, partial_sum in pairs:
|
||||
anims1 = [
|
||||
ShowCreation(sine_graph)
|
||||
]
|
||||
partial_sum.set_stroke(BLACK, 4, background=True)
|
||||
anims2 = [
|
||||
curr_partial_sum.set_stroke,
|
||||
{"width": 1, "opacity": 0.25},
|
||||
curr_partial_sum.set_stroke,
|
||||
{"width": 0, "background": True},
|
||||
ReplacementTransform(
|
||||
sine_graph, partial_sum,
|
||||
remover=True
|
||||
),
|
||||
]
|
||||
self.play(*anims1)
|
||||
self.play(*anims2)
|
||||
curr_partial_sum = partial_sum
|
||||
|
||||
#
|
||||
def setup_axes(self):
|
||||
super().setup_axes()
|
||||
self.axes.shift(
|
||||
self.axes.c2p(0, 0)[1] * DOWN
|
||||
)
|
||||
|
||||
|
||||
class BreakDownStepFunction(StepFunctionExample):
|
||||
CONFIG = {
|
||||
"axes_config": {
|
||||
"x_axis_config": {
|
||||
"stroke_width": 2,
|
||||
},
|
||||
"y_axis_config": {
|
||||
"tick_frequency": 0.25,
|
||||
"stroke_width": 2,
|
||||
},
|
||||
"y_min": -1.25,
|
||||
"y_max": 1.25,
|
||||
},
|
||||
"alpha": 0.1,
|
||||
"wait_time": 30,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.setup_axes()
|
||||
self.setup_graph()
|
||||
self.setup_clock()
|
||||
self.add_rod()
|
||||
|
||||
self.wait()
|
||||
self.init_updaters()
|
||||
self.play(
|
||||
self.get_clock_anim(self.wait_time)
|
||||
)
|
||||
|
||||
def setup_axes(self):
|
||||
super().setup_axes()
|
||||
axes = self.axes
|
||||
axes.to_edge(LEFT)
|
||||
|
||||
mini_axes = VGroup(*[
|
||||
axes.deepcopy()
|
||||
for x in range(4)
|
||||
])
|
||||
for n, ma in zip(it.count(1, 2), mini_axes):
|
||||
if n == 1:
|
||||
t1 = TexMobject("1")
|
||||
t2 = TexMobject("-1")
|
||||
else:
|
||||
t1 = TexMobject("1 / " + str(n))
|
||||
t2 = TexMobject("-1 / " + str(n))
|
||||
VGroup(t1, t2).scale(1.5)
|
||||
t1.next_to(ma.y_axis.n2p(1), LEFT, MED_SMALL_BUFF)
|
||||
t2.next_to(ma.y_axis.n2p(-1), LEFT, MED_SMALL_BUFF)
|
||||
ma.y_axis.numbers.set_opacity(0)
|
||||
ma.y_axis.add(t1, t2)
|
||||
|
||||
for mob in mini_axes.get_family():
|
||||
if isinstance(mob, Line):
|
||||
mob.set_stroke(width=1, family=False)
|
||||
mini_axes.arrange(DOWN, buff=2)
|
||||
mini_axes.set_height(FRAME_HEIGHT - 1.5)
|
||||
mini_axes.to_corner(UR)
|
||||
self.scale_factor = fdiv(
|
||||
mini_axes[0].get_width(),
|
||||
axes.get_width(),
|
||||
)
|
||||
|
||||
# mini_axes.arrange(RIGHT, buff=2)
|
||||
# mini_axes.set_width(FRAME_WIDTH - 1.5)
|
||||
# mini_axes.to_edge(LEFT)
|
||||
|
||||
dots = TexMobject("\\vdots")
|
||||
dots.next_to(mini_axes, DOWN)
|
||||
dots.shift_onto_screen()
|
||||
|
||||
self.add(axes)
|
||||
self.add(mini_axes)
|
||||
self.add(dots)
|
||||
|
||||
self.mini_axes = mini_axes
|
||||
|
||||
def setup_graph(self):
|
||||
super().setup_graph()
|
||||
graph = self.graph
|
||||
self.add(graph)
|
||||
|
||||
mini_axes = self.mini_axes
|
||||
mini_graphs = VGroup()
|
||||
for axes, u, n in zip(mini_axes, it.cycle([1, -1]), it.count(1, 2)):
|
||||
mini_graph = axes.get_graph(
|
||||
lambda x: (4 / PI) * (u / 1) * np.cos(PI * n * x),
|
||||
)
|
||||
mini_graph.set_stroke(WHITE, width=2)
|
||||
mini_graphs.add(mini_graph)
|
||||
# mini_graphs.set_color_by_gradient(
|
||||
# BLUE, GREEN, RED, YELLOW,
|
||||
# )
|
||||
|
||||
self.mini_graphs = mini_graphs
|
||||
self.add(mini_graphs)
|
||||
|
||||
def setup_clock(self):
|
||||
super().setup_clock()
|
||||
clock = self.clock
|
||||
time_label = self.time_label
|
||||
|
||||
clock.move_to(3 * RIGHT)
|
||||
clock.to_corner(UP)
|
||||
time_label.next_to(clock, DOWN)
|
||||
|
||||
self.add(clock)
|
||||
self.add(time_label)
|
||||
|
||||
def add_rod(self):
|
||||
self.rod = self.get_rod(0, 1)
|
||||
self.add(self.rod)
|
||||
|
||||
def init_updaters(self):
|
||||
self.graph.add_updater(self.update_graph)
|
||||
for mg in self.mini_graphs:
|
||||
mg.add_updater(
|
||||
lambda m, dt: self.update_graph(
|
||||
m, dt,
|
||||
alpha=self.scale_factor * self.alpha
|
||||
)
|
||||
)
|
||||
self.time_label.add_updater(
|
||||
lambda d, dt: d.increment_value(dt)
|
||||
)
|
||||
self.rod.add_updater(
|
||||
lambda r: self.update_rods([r])
|
||||
)
|
||||
474
from_3b1b/active/diffyq/part4/three_d_graphs.py
Normal file
474
from_3b1b/active/diffyq/part4/three_d_graphs.py
Normal file
@@ -0,0 +1,474 @@
|
||||
from manimlib.imports import *
|
||||
from active_projects.diffyq.part3.temperature_graphs import TemperatureGraphScene
|
||||
from active_projects.diffyq.part2.wordy_scenes import WriteHeatEquationTemplate
|
||||
|
||||
|
||||
class ShowLinearity(WriteHeatEquationTemplate, TemperatureGraphScene):
|
||||
CONFIG = {
|
||||
"temp_text": "Temp",
|
||||
"alpha": 0.1,
|
||||
"axes_config": {
|
||||
"z_max": 2,
|
||||
"z_min": -2,
|
||||
"z_axis_config": {
|
||||
"tick_frequency": 0.5,
|
||||
"unit_size": 1.5,
|
||||
},
|
||||
},
|
||||
"default_surface_config": {
|
||||
"resolution": (16, 16)
|
||||
# "resolution": (4, 4)
|
||||
},
|
||||
"freqs": [2, 5],
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
TemperatureGraphScene.setup(self)
|
||||
WriteHeatEquationTemplate.setup(self)
|
||||
|
||||
def construct(self):
|
||||
self.init_camera()
|
||||
self.add_three_graphs()
|
||||
self.show_words()
|
||||
self.add_function_labels()
|
||||
self.change_scalars()
|
||||
|
||||
def init_camera(self):
|
||||
self.camera.set_distance(1000)
|
||||
|
||||
def add_three_graphs(self):
|
||||
axes_group = self.get_axes_group()
|
||||
axes0, axes1, axes2 = axes_group
|
||||
freqs = self.freqs
|
||||
scalar_trackers = Group(
|
||||
ValueTracker(1),
|
||||
ValueTracker(1),
|
||||
)
|
||||
graphs = VGroup(
|
||||
self.get_graph(axes0, [freqs[0]], [scalar_trackers[0]]),
|
||||
self.get_graph(axes1, [freqs[1]], [scalar_trackers[1]]),
|
||||
self.get_graph(axes2, freqs, scalar_trackers),
|
||||
)
|
||||
|
||||
plus = TexMobject("+").scale(2)
|
||||
equals = TexMobject("=").scale(2)
|
||||
plus.move_to(midpoint(
|
||||
axes0.get_right(),
|
||||
axes1.get_left(),
|
||||
))
|
||||
equals.move_to(midpoint(
|
||||
axes1.get_right(),
|
||||
axes2.get_left(),
|
||||
))
|
||||
|
||||
self.add(axes_group)
|
||||
self.add(graphs)
|
||||
self.add(plus)
|
||||
self.add(equals)
|
||||
|
||||
self.axes_group = axes_group
|
||||
self.graphs = graphs
|
||||
self.scalar_trackers = scalar_trackers
|
||||
self.plus = plus
|
||||
self.equals = equals
|
||||
|
||||
def show_words(self):
|
||||
equation = self.get_d1_equation()
|
||||
name = TextMobject("Heat equation")
|
||||
name.next_to(equation, DOWN)
|
||||
name.set_color_by_gradient(RED, YELLOW)
|
||||
group = VGroup(equation, name)
|
||||
group.to_edge(UP)
|
||||
|
||||
shift_val = 0.5 * RIGHT
|
||||
|
||||
arrow = Vector(1.5 * RIGHT)
|
||||
arrow.move_to(group)
|
||||
arrow.shift(shift_val)
|
||||
linear_word = TextMobject("``Linear''")
|
||||
linear_word.scale(2)
|
||||
linear_word.next_to(arrow, RIGHT)
|
||||
|
||||
self.add(group)
|
||||
self.wait()
|
||||
self.play(
|
||||
ShowCreation(arrow),
|
||||
group.next_to, arrow, LEFT
|
||||
)
|
||||
self.play(FadeInFrom(linear_word, LEFT))
|
||||
self.wait()
|
||||
|
||||
def add_function_labels(self):
|
||||
axes_group = self.axes_group
|
||||
graphs = self.graphs
|
||||
|
||||
solution_labels = VGroup()
|
||||
for axes in axes_group:
|
||||
label = TextMobject("Solution", "$\\checkmark$")
|
||||
label.set_color_by_tex("checkmark", GREEN)
|
||||
label.next_to(axes, DOWN)
|
||||
solution_labels.add(label)
|
||||
|
||||
kw = {
|
||||
"tex_to_color_map": {
|
||||
"T_1": BLUE,
|
||||
"T_2": GREEN,
|
||||
}
|
||||
}
|
||||
T1 = TexMobject("a", "T_1", **kw)
|
||||
T2 = TexMobject("b", "T_2", **kw)
|
||||
T_sum = TexMobject("T_1", "+", "T_2", **kw)
|
||||
T_sum_with_scalars = TexMobject(
|
||||
"a", "T_1", "+", "b", "T_2", **kw
|
||||
)
|
||||
|
||||
T1.next_to(graphs[0], UP)
|
||||
T2.next_to(graphs[1], UP)
|
||||
T_sum.next_to(graphs[2], UP)
|
||||
T_sum.shift(SMALL_BUFF * DOWN)
|
||||
T_sum_with_scalars.move_to(T_sum)
|
||||
|
||||
a_brace = Brace(T1[0], UP, buff=SMALL_BUFF)
|
||||
b_brace = Brace(T2[0], UP, buff=SMALL_BUFF)
|
||||
s1_decimal = DecimalNumber()
|
||||
s1_decimal.match_color(T1[1])
|
||||
s1_decimal.next_to(a_brace, UP, SMALL_BUFF)
|
||||
s1_decimal.add_updater(lambda m: m.set_value(
|
||||
self.scalar_trackers[0].get_value()
|
||||
))
|
||||
s2_decimal = DecimalNumber()
|
||||
s2_decimal.match_color(T2[1])
|
||||
s2_decimal.next_to(b_brace, UP, SMALL_BUFF)
|
||||
s2_decimal.add_updater(lambda m: m.set_value(
|
||||
self.scalar_trackers[1].get_value()
|
||||
))
|
||||
|
||||
self.play(
|
||||
FadeInFrom(T1[1], DOWN),
|
||||
FadeInFrom(solution_labels[0], UP),
|
||||
)
|
||||
self.play(
|
||||
FadeInFrom(T2[1], DOWN),
|
||||
FadeInFrom(solution_labels[1], UP),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
TransformFromCopy(T1[1], T_sum[0]),
|
||||
TransformFromCopy(T2[1], T_sum[2]),
|
||||
TransformFromCopy(self.plus, T_sum[1]),
|
||||
*[
|
||||
Transform(
|
||||
graph.copy().set_fill(opacity=0),
|
||||
graphs[2].copy().set_fill(opacity=0),
|
||||
remover=True
|
||||
)
|
||||
for graph in graphs[:2]
|
||||
]
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeInFrom(solution_labels[2], UP))
|
||||
self.wait()
|
||||
|
||||
# Show constants
|
||||
self.play(
|
||||
FadeIn(T1[0]),
|
||||
FadeIn(T2[0]),
|
||||
FadeIn(a_brace),
|
||||
FadeIn(b_brace),
|
||||
FadeIn(s1_decimal),
|
||||
FadeIn(s2_decimal),
|
||||
FadeOut(T_sum),
|
||||
FadeIn(T_sum_with_scalars),
|
||||
)
|
||||
|
||||
def change_scalars(self):
|
||||
s1, s2 = self.scalar_trackers
|
||||
|
||||
kw = {
|
||||
"run_time": 2,
|
||||
}
|
||||
for graph in self.graphs:
|
||||
graph.resume_updating()
|
||||
self.play(s2.set_value, -0.5, **kw)
|
||||
self.play(s1.set_value, -0.2, **kw)
|
||||
self.play(s2.set_value, 1.5, **kw)
|
||||
self.play(s1.set_value, 1.2, **kw)
|
||||
self.play(s2.set_value, 0.3, **kw)
|
||||
self.wait()
|
||||
|
||||
#
|
||||
def get_axes_group(self):
|
||||
axes_group = VGroup(*[
|
||||
self.get_axes()
|
||||
for x in range(3)
|
||||
])
|
||||
axes_group.arrange(RIGHT, buff=2)
|
||||
axes_group.set_width(FRAME_WIDTH - 1)
|
||||
axes_group.to_edge(DOWN, buff=1)
|
||||
return axes_group
|
||||
|
||||
def get_axes(self, **kwargs):
|
||||
axes = self.get_three_d_axes(**kwargs)
|
||||
# axes.input_plane.set_fill(opacity=0)
|
||||
# axes.input_plane.set_stroke(width=0.5)
|
||||
# axes.add(axes.input_plane)
|
||||
self.orient_three_d_mobject(axes)
|
||||
axes.rotate(-5 * DEGREES, UP)
|
||||
axes.set_width(4)
|
||||
axes.x_axis.label.next_to(
|
||||
axes.x_axis.get_end(), DOWN,
|
||||
buff=2 * SMALL_BUFF
|
||||
)
|
||||
return axes
|
||||
|
||||
def get_graph(self, axes, freqs, scalar_trackers):
|
||||
L = axes.x_max
|
||||
a = self.alpha
|
||||
|
||||
def func(x, t):
|
||||
scalars = [st.get_value() for st in scalar_trackers]
|
||||
return np.sum([
|
||||
s * np.cos(k * x) * np.exp(-a * (k**2) * t)
|
||||
for freq, s in zip(freqs, scalars)
|
||||
for k in [freq * PI / L]
|
||||
])
|
||||
|
||||
def get_surface_graph_group():
|
||||
return VGroup(
|
||||
self.get_surface(axes, func),
|
||||
self.get_time_slice_graph(axes, func, t=0),
|
||||
)
|
||||
|
||||
result = always_redraw(get_surface_graph_group)
|
||||
result.func = func
|
||||
result.suspend_updating()
|
||||
return result
|
||||
|
||||
|
||||
class CombineSeveralSolutions(ShowLinearity):
|
||||
CONFIG = {
|
||||
"default_surface_config": {
|
||||
"resolution": (16, 16),
|
||||
# "resolution": (4, 4),
|
||||
},
|
||||
"n_top_graphs": 5,
|
||||
"axes_config": {
|
||||
"y_max": 15,
|
||||
},
|
||||
"target_scalars": [
|
||||
0.81, -0.53, 0.41, 0.62, -0.95
|
||||
],
|
||||
"final_run_time": 14,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.init_camera()
|
||||
self.add_all_axes()
|
||||
self.setup_all_graphs()
|
||||
self.show_infinite_family()
|
||||
self.show_sum()
|
||||
self.show_time_passing()
|
||||
|
||||
def add_all_axes(self):
|
||||
top_axes_group = VGroup(*[
|
||||
self.get_axes(
|
||||
z_min=-1.25,
|
||||
z_max=1.25,
|
||||
z_axis_config={
|
||||
"unit_size": 2,
|
||||
"tick_frequency": 0.5,
|
||||
},
|
||||
)
|
||||
for x in range(self.n_top_graphs)
|
||||
])
|
||||
top_axes_group.arrange(RIGHT, buff=2)
|
||||
top_axes_group.set_width(FRAME_WIDTH - 1.5)
|
||||
top_axes_group.to_corner(UL)
|
||||
dots = TexMobject("\\dots")
|
||||
dots.next_to(top_axes_group, RIGHT)
|
||||
|
||||
low_axes = self.get_axes()
|
||||
low_axes.center()
|
||||
low_axes.scale(1.2)
|
||||
low_axes.to_edge(DOWN, buff=SMALL_BUFF)
|
||||
|
||||
self.add(top_axes_group)
|
||||
self.add(dots)
|
||||
self.add(low_axes)
|
||||
|
||||
self.top_axes_group = top_axes_group
|
||||
self.low_axes = low_axes
|
||||
|
||||
def setup_all_graphs(self):
|
||||
scalar_trackers = Group(*[
|
||||
ValueTracker(1)
|
||||
for x in range(self.n_top_graphs)
|
||||
])
|
||||
freqs = np.arange(self.n_top_graphs)
|
||||
freqs += 1
|
||||
self.top_graphs = VGroup(*[
|
||||
self.get_graph(axes, [n], [st])
|
||||
for axes, n, st in zip(
|
||||
self.top_axes_group,
|
||||
freqs,
|
||||
scalar_trackers,
|
||||
)
|
||||
])
|
||||
self.low_graph = self.get_graph(
|
||||
self.low_axes, freqs, scalar_trackers
|
||||
)
|
||||
|
||||
self.scalar_trackers = scalar_trackers
|
||||
|
||||
def show_infinite_family(self):
|
||||
top_axes_group = self.top_axes_group
|
||||
top_graphs = self.top_graphs
|
||||
scalar_trackers = self.scalar_trackers
|
||||
|
||||
decimals = self.get_decimals(
|
||||
top_axes_group, scalar_trackers
|
||||
)
|
||||
|
||||
self.play(LaggedStart(*[
|
||||
AnimationGroup(
|
||||
Write(graph[0]),
|
||||
FadeIn(graph[1]),
|
||||
)
|
||||
for graph in top_graphs
|
||||
]))
|
||||
self.wait()
|
||||
self.play(FadeIn(decimals))
|
||||
for graph in top_graphs:
|
||||
graph.resume_updating()
|
||||
|
||||
self.play(LaggedStart(*[
|
||||
ApplyMethod(st.set_value, value)
|
||||
for st, value in zip(
|
||||
scalar_trackers,
|
||||
self.target_scalars,
|
||||
)
|
||||
]), run_time=3)
|
||||
self.wait()
|
||||
|
||||
def show_sum(self):
|
||||
top_graphs = self.top_graphs
|
||||
low_graph = self.low_graph
|
||||
low_graph.resume_updating()
|
||||
low_graph.update()
|
||||
|
||||
self.play(
|
||||
LaggedStart(*[
|
||||
Transform(
|
||||
top_graph.copy().set_fill(opacity=0),
|
||||
low_graph.copy().set_fill(opacity=0),
|
||||
remover=True,
|
||||
)
|
||||
for top_graph in top_graphs
|
||||
]),
|
||||
FadeIn(
|
||||
low_graph,
|
||||
rate_func=squish_rate_func(smooth, 0.7, 1)
|
||||
),
|
||||
run_time=3,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def show_time_passing(self):
|
||||
all_graphs = [*self.top_graphs, self.low_graph]
|
||||
all_axes = [*self.top_axes_group, self.low_axes]
|
||||
|
||||
time_tracker = ValueTracker(0)
|
||||
get_t = time_tracker.get_value
|
||||
|
||||
anims = [
|
||||
ApplyMethod(
|
||||
time_tracker.set_value, 1,
|
||||
run_time=1,
|
||||
rate_func=linear
|
||||
)
|
||||
]
|
||||
|
||||
for axes, graph_group in zip(all_axes, all_graphs):
|
||||
graph_group.clear_updaters()
|
||||
surface, gslice = graph_group
|
||||
plane = self.get_const_time_plane(axes)
|
||||
plane.t_tracker.add_updater(
|
||||
lambda m: m.set_value(get_t())
|
||||
)
|
||||
gslice.axes = axes
|
||||
gslice.func = graph_group.func
|
||||
gslice.add_updater(lambda m: m.become(
|
||||
self.get_time_slice_graph(
|
||||
m.axes, m.func, t=get_t()
|
||||
)
|
||||
))
|
||||
self.add(gslice)
|
||||
self.add(plane.t_tracker)
|
||||
anims.append(FadeIn(plane))
|
||||
|
||||
self.play(*anims)
|
||||
run_time = self.final_run_time
|
||||
self.play(
|
||||
time_tracker.increment_value, run_time,
|
||||
run_time=run_time,
|
||||
rate_func=linear,
|
||||
)
|
||||
|
||||
#
|
||||
def get_decimals(self, axes_group, scalar_trackers):
|
||||
result = VGroup()
|
||||
for axes, st in zip(axes_group, scalar_trackers):
|
||||
decimal = DecimalNumber()
|
||||
decimal.move_to(axes.get_bottom(), UP)
|
||||
decimal.shift(SMALL_BUFF * RIGHT)
|
||||
decimal.set_color(YELLOW)
|
||||
decimal.scalar_tracker = st
|
||||
times = TexMobject("\\times")
|
||||
times.next_to(decimal, LEFT, SMALL_BUFF)
|
||||
decimal.add_updater(lambda d: d.set_value(
|
||||
d.scalar_tracker.get_value()
|
||||
))
|
||||
group = VGroup(times, decimal)
|
||||
group.scale(0.7)
|
||||
result.add(group)
|
||||
return result
|
||||
|
||||
|
||||
class CycleThroughManyLinearCombinations(CombineSeveralSolutions):
|
||||
CONFIG = {
|
||||
"default_surface_config": {
|
||||
"resolution": (16, 16),
|
||||
# "resolution": (4, 4),
|
||||
},
|
||||
"n_cycles": 10,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.init_camera()
|
||||
self.add_all_axes()
|
||||
self.setup_all_graphs()
|
||||
#
|
||||
self.cycle_through_superpositions()
|
||||
|
||||
def cycle_through_superpositions(self):
|
||||
top_graphs = self.top_graphs
|
||||
low_graph = self.low_graph
|
||||
scalar_trackers = self.scalar_trackers
|
||||
self.add(self.get_decimals(
|
||||
self.top_axes_group, scalar_trackers
|
||||
))
|
||||
|
||||
for graph in [low_graph, *top_graphs]:
|
||||
graph.resume_updating()
|
||||
self.add(graph)
|
||||
|
||||
nst = len(scalar_trackers)
|
||||
for x in range(self.n_cycles):
|
||||
self.play(LaggedStart(*[
|
||||
ApplyMethod(st.set_value, value)
|
||||
for st, value in zip(
|
||||
scalar_trackers,
|
||||
3 * np.random.random(nst) - 1.5
|
||||
)
|
||||
]), run_time=3)
|
||||
self.wait()
|
||||
1586
from_3b1b/active/diffyq/part5/staging.py
Normal file
1586
from_3b1b/active/diffyq/part5/staging.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
from manimlib.imports import *
|
||||
|
||||
from old_projects.crypto import sha256_tex_mob, bit_string_to_mobject, BitcoinLogo
|
||||
from from_3b1b.old.crypto import sha256_tex_mob, bit_string_to_mobject, BitcoinLogo
|
||||
|
||||
def get_google_logo():
|
||||
result = SVGMobject(
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
from manimlib.imports import *
|
||||
|
||||
from old_projects.uncertainty import Flash
|
||||
from old_projects.WindingNumber import *
|
||||
from from_3b1b.old.uncertainty import Flash
|
||||
from from_3b1b.old.WindingNumber import *
|
||||
|
||||
|
||||
# Warning, this file uses ContinualChangingDecimal,
|
||||
@@ -746,7 +746,7 @@ class StartingCalc101(PiCreatureScene):
|
||||
def get_aha_image(self):
|
||||
creature = self.pi_creature.copy()
|
||||
creature.change_mode("hooray")
|
||||
from old_projects.eoc.chapter3 import NudgeSideLengthOfCube
|
||||
from from_3b1b.old.eoc.chapter3 import NudgeSideLengthOfCube
|
||||
scene = NudgeSideLengthOfCube(
|
||||
end_at_animation_number=7,
|
||||
skip_animations=True
|
||||
@@ -1,7 +1,7 @@
|
||||
from manimlib.imports import *
|
||||
from old_projects.lost_lecture import GeometryProofLand
|
||||
from old_projects.quaternions import SpecialThreeDScene
|
||||
from old_projects.uncertainty import Flash
|
||||
from from_3b1b.old.lost_lecture import GeometryProofLand
|
||||
from from_3b1b.old.quaternions import SpecialThreeDScene
|
||||
from from_3b1b.old.uncertainty import Flash
|
||||
|
||||
|
||||
class Introduction(TeacherStudentsScene):
|
||||
@@ -807,7 +807,7 @@ class FunctionGOutputSpace(FunctionGInputSpace):
|
||||
x_max=2.5,
|
||||
y_min=-2.5,
|
||||
y_max=2.5,
|
||||
number_line_config={'unit_size': 1.5}
|
||||
axis_config={'unit_size': 1.5}
|
||||
)
|
||||
for axis in axes:
|
||||
numbers = list(range(-2, 3))
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from old_projects.brachistochrone.curves import *
|
||||
from from_3b1b.old.brachistochrone.curves import *
|
||||
|
||||
class RollAlongVector(Animation):
|
||||
CONFIG = {
|
||||
@@ -3,7 +3,7 @@ import itertools as it
|
||||
|
||||
from manimlib.imports import *
|
||||
|
||||
from old_projects.brachistochrone.curves import Cycloid
|
||||
from from_3b1b.old.brachistochrone.curves import Cycloid
|
||||
|
||||
class MultilayeredGlass(PhotonScene, ZoomedScene):
|
||||
CONFIG = {
|
||||
@@ -3,7 +3,7 @@ import itertools as it
|
||||
|
||||
from manimlib.imports import *
|
||||
|
||||
from old_projects.brachistochrone.curves import \
|
||||
from from_3b1b.old.brachistochrone.curves import \
|
||||
Cycloid, PathSlidingScene, RANDY_SCALE_FACTOR, TryManyPaths
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import numpy as np
|
||||
import itertools as it
|
||||
|
||||
from manimlib.imports import *
|
||||
from old_projects.brachistochrone.curves import Cycloid
|
||||
from from_3b1b.old.brachistochrone.curves import Cycloid
|
||||
|
||||
class PhysicalIntuition(Scene):
|
||||
def construct(self):
|
||||
@@ -2,8 +2,8 @@ import numpy as np
|
||||
import itertools as it
|
||||
|
||||
from manimlib.imports import *
|
||||
from old_projects.brachistochrone.light import PhotonScene
|
||||
from old_projects.brachistochrone.curves import *
|
||||
from from_3b1b.old.brachistochrone.light import PhotonScene
|
||||
from from_3b1b.old.brachistochrone.curves import *
|
||||
|
||||
|
||||
class MultilayeredScene(Scene):
|
||||
@@ -3,7 +3,7 @@ import itertools as it
|
||||
import os
|
||||
|
||||
from manimlib.imports import *
|
||||
from old_projects.brachistochrone.drawing_images import sort_by_color
|
||||
from from_3b1b.old.brachistochrone.drawing_images import sort_by_color
|
||||
|
||||
class Intro(Scene):
|
||||
def construct(self):
|
||||
@@ -1,10 +1,10 @@
|
||||
from old_projects.clacks import question
|
||||
from old_projects.clacks.solution2 import block_collision_scenes
|
||||
from old_projects.clacks.solution2 import mirror_scenes
|
||||
from old_projects.clacks.solution2 import pi_creature_scenes
|
||||
from old_projects.clacks.solution2 import position_phase_space
|
||||
from old_projects.clacks.solution2 import simple_scenes
|
||||
from old_projects.clacks.solution2 import wordy_scenes
|
||||
from from_3b1b.old.clacks import question
|
||||
from from_3b1b.old.clacks.solution2 import block_collision_scenes
|
||||
from from_3b1b.old.clacks.solution2 import mirror_scenes
|
||||
from from_3b1b.old.clacks.solution2 import pi_creature_scenes
|
||||
from from_3b1b.old.clacks.solution2 import position_phase_space
|
||||
from from_3b1b.old.clacks.solution2 import simple_scenes
|
||||
from from_3b1b.old.clacks.solution2 import wordy_scenes
|
||||
|
||||
OUTPUT_DIRECTORY = "clacks/solution2"
|
||||
SCENES_IN_ORDER = [
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
from manimlib.imports import *
|
||||
from old_projects.clacks.question import BlocksAndWallExample
|
||||
from from_3b1b.old.clacks.question import BlocksAndWallExample
|
||||
|
||||
|
||||
class NameBump(BlocksAndWallExample):
|
||||
@@ -325,8 +325,8 @@ class BlocksAndWallScene(Scene):
|
||||
counter_mob = Integer(self.n_clacks)
|
||||
counter_mob.next_to(
|
||||
counter_label[-1], RIGHT,
|
||||
aligned_edge=DOWN,
|
||||
)
|
||||
counter_mob.align_to(counter_label[-1][-1], DOWN)
|
||||
counter_group = VGroup(
|
||||
counter_label,
|
||||
counter_mob,
|
||||
@@ -747,7 +747,7 @@ class BlocksAndWallExampleMass1e2(BlocksAndWallExample):
|
||||
"velocity": -0.6,
|
||||
}
|
||||
},
|
||||
"wait_time": 25,
|
||||
"wait_time": 35,
|
||||
}
|
||||
|
||||
|
||||
@@ -888,7 +888,7 @@ class PiComputingAlgorithmsAxes(Scene):
|
||||
y_min=0,
|
||||
x_max=9,
|
||||
y_max=5,
|
||||
number_line_config={
|
||||
axis_config={
|
||||
"tick_frequency": 100,
|
||||
"numbers_with_elongated_ticks": [],
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
from manimlib.imports import *
|
||||
from old_projects.clacks.question import *
|
||||
from old_projects.div_curl import ShowTwoPopulations
|
||||
from from_3b1b.old.clacks.question import *
|
||||
from from_3b1b.old.div_curl import ShowTwoPopulations
|
||||
|
||||
|
||||
OUTPUT_DIRECTORY = "clacks/solution1"
|
||||
@@ -633,7 +633,7 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities):
|
||||
"x_min": -3.5,
|
||||
"x_max": 4,
|
||||
},
|
||||
"number_line_config": {
|
||||
"axis_config": {
|
||||
"unit_size": 0.7,
|
||||
},
|
||||
},
|
||||
@@ -1204,6 +1204,8 @@ class CircleDiagramFromSlidingBlocks(Scene):
|
||||
"fill_color": GREEN,
|
||||
"fill_opacity": 0.3,
|
||||
},
|
||||
"show_dot": True,
|
||||
"show_vector": False,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
@@ -1211,6 +1213,9 @@ class CircleDiagramFromSlidingBlocks(Scene):
|
||||
show_flash_animations=False,
|
||||
write_to_movie=False,
|
||||
wait_time=0,
|
||||
file_writer_config={
|
||||
"output_directory": ".",
|
||||
}
|
||||
)
|
||||
blocks = sliding_blocks_scene.blocks
|
||||
times = [pair[1] for pair in blocks.clack_data]
|
||||
@@ -1229,7 +1234,17 @@ class CircleDiagramFromSlidingBlocks(Scene):
|
||||
dot = Dot(color=RED, radius=0.06)
|
||||
dot.move_to(lines[0].get_start())
|
||||
|
||||
self.add(end_zone, axes, circle, dot)
|
||||
vector = Vector(lines[0].get_start())
|
||||
vector.set_color(RED)
|
||||
vector.add_updater(lambda v: v.put_start_and_end_on(
|
||||
ORIGIN, dot.get_center()
|
||||
))
|
||||
vector.set_stroke(BLACK, 2, background=True)
|
||||
|
||||
dot.set_opacity(int(self.show_dot))
|
||||
vector.set_opacity(int(self.show_vector))
|
||||
|
||||
self.add(end_zone, axes, circle, dot, vector)
|
||||
|
||||
last_time = 0
|
||||
for time, line in zip(times, lines):
|
||||
@@ -1238,7 +1253,7 @@ class CircleDiagramFromSlidingBlocks(Scene):
|
||||
self.wait(time - last_time)
|
||||
last_time = time
|
||||
dot.move_to(line.get_end())
|
||||
self.add(line, dot)
|
||||
self.add(line, dot, vector)
|
||||
self.wait()
|
||||
|
||||
def get_circle(self):
|
||||
@@ -2597,7 +2612,7 @@ class UnitCircleIntuition(Scene):
|
||||
def draw_unit_circle(self):
|
||||
unit_size = 2.5
|
||||
axes = Axes(
|
||||
number_line_config={"unit_size": unit_size},
|
||||
axis_config={"unit_size": unit_size},
|
||||
x_min=-2.5, x_max=2.5,
|
||||
y_min=-1.5, y_max=1.5,
|
||||
)
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from old_projects.clacks.question import BlocksAndWallExample
|
||||
from from_3b1b.old.clacks.question import BlocksAndWallExample
|
||||
|
||||
|
||||
class PreviousTwoVideos(BlocksAndWallExample):
|
||||
@@ -1,7 +1,7 @@
|
||||
from manimlib.imports import *
|
||||
from old_projects.clacks.question import Block
|
||||
from old_projects.clacks.question import Wall
|
||||
from old_projects.clacks.question import ClackFlashes
|
||||
from from_3b1b.old.clacks.question import Block
|
||||
from from_3b1b.old.clacks.question import Wall
|
||||
from from_3b1b.old.clacks.question import ClackFlashes
|
||||
|
||||
|
||||
class PositionPhaseSpaceScene(Scene):
|
||||
@@ -1,7 +1,7 @@
|
||||
from manimlib.imports import *
|
||||
from old_projects.lost_lecture import ShowWord
|
||||
from old_projects.clacks.solution2.mirror_scenes import ReflectWorldThroughMirrorNew
|
||||
from old_projects.clacks.question import Thumbnail
|
||||
from from_3b1b.old.lost_lecture import ShowWord
|
||||
from from_3b1b.old.clacks.solution2.mirror_scenes import ReflectWorldThroughMirrorNew
|
||||
from from_3b1b.old.clacks.question import Thumbnail
|
||||
|
||||
|
||||
class WrapperScene(Scene):
|
||||
@@ -1,5 +1,5 @@
|
||||
from manimlib.imports import *
|
||||
from old_projects.clacks.solution2.position_phase_space import ShowMomentumConservation
|
||||
from from_3b1b.old.clacks.solution2.position_phase_space import ShowMomentumConservation
|
||||
|
||||
|
||||
class ConnectionToOptics(Scene):
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user