This commit is contained in:
techdude 2021-01-31 18:52:17 -07:00
commit fc482c107f
432 changed files with 10582 additions and 285141 deletions

36
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: docs
on:
push:
paths:
- 'docs/**'
jobs:
docs:
runs-on: ubuntu-latest
name: build up document and deploy
steps:
- name: Checkout
uses: actions/checkout@master
- name: Install sphinx and manim env
run: |
pip3 install --upgrade pip
sudo apt install python3-setuptools
pip3 install -r docs/requirements.txt
pip3 install -r requirements.txt
- name: Build document with Sphinx
run: |
cd docs
export PATH="$PATH:/home/runner/.local/bin"
export SPHINXBUILD="python3 -m sphinx"
make html
- name: Deploy to GitHub pages
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
ACCESS_TOKEN: ${{ secrets.DOC_DEPLOY_TOKEN }}
BRANCH: gh-pages
FOLDER: docs/build/html

29
.github/workflows/docs_test.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: docs_test
on:
pull_request:
paths:
- 'docs/**'
jobs:
docs_test:
runs-on: ubuntu-latest
name: Test document for pull requests
steps:
- name: Checkout
uses: actions/checkout@master
- name: Install sphinx and manim env
run: |
pip3 install --upgrade pip
sudo apt install python3-setuptools
pip3 install -r docs/requirements.txt
pip3 install -r requirements.txt
- name: Build document with Sphinx
run: |
cd docs
export PATH="$PATH:/home/runner/.local/bin"
export SPHINXBUILD="python3 -m sphinx"
make html

175
.gitignore vendored
View file

@ -1,28 +1,149 @@
*.pyc
*.bak
.DS_Store
homeless.py
playground.py
cairo_test.py
mayavi_test.py
random_scenes/
files/
assets/
ben_playground.py
ben_cairo_test.py
.floo
.flooignore
.vscode
.vs
*.xml
*.iml
media
manim.sublime-project
manim.sublime-workspace
.eggs/
build/
dist/
manim.egg-info/
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python
primes.py
/media_dir.txt
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
pytestdebug.log
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
doc/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
pythonenv*
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# profiling data
.prof
# End of https://www.toptal.com/developers/gitignore/api/python
# Custom exclusions:
.DS_Store
#
/videos

View file

@ -1,30 +0,0 @@
language: python
dist: bionic
python: "3.7"
cache: pip
addons:
apt:
packages:
- python3-sphinx
install:
- pip install --upgrade pip
- pip install -r requirements.txt
- pip install flake8
before_script:
# stop the build if there are Python syntax errors or undefined names
- flake8 manimlib/ --count --select=E9,F63,F72,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
- flake8 manimlib/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
script:
- python setup.py test
- python setup.py bdist_wheel
after_success:
- test $TRAVIS_BRANCH = "master" && test $TRAVIS_PULL_REQUEST = "false" && travis/build_docs.sh
deploy:
provider: pypi
user: eulertour
on:
tags: true
password:
secure: j5M2hiJo9kDWJhl0/iSuIQmfd2G2O1Qoc455AkUPMCheAcALnX9xJgFsYBmqfgOXTCtUCQf52XGdOIG4o4s5TY340NZ9eLKI9cWae+sTeSrDCkdwChUilm3D0jQf1FWPUf9ywScwGi20m0sRtzxEJyTuX+JMFd7PIa8bFoDXWPtEjoFOOJrfBusMsANzrI+j+vIMdJ48lc1J8UsQdZapwusTrYU9s12JLhKBPLavmaDKf0HDAJdEhFQ9SaINdkiW/QY8qbfJ/MVu5jHai168zXjD/IaswxoKqCO1G+fWlOq3KwVhG7gI7rwhnnuF+wcA7yLAaMdo0CjO2V7z15S6cG721V2Il2IIh1jq0F8irSH1ZOLOkv/fFk9hkSUQyEU0i8k4m1wE9L47a6GP/66+b+gI91PGfxBOqq4gE/1BdZJqceh0qc13KpcehtYrQwR05bSw0Ye5OoTkqAnCeON0B0Ur4ejfHd3TzkjgB06fw76cZtjAK8f/YjB3KyNCvysOixgzE4tRxlY92yX/tAKZ3iX3yD0MjsinSfwo52N5sIEaCS/FmPRMhJOQBa6ftkfbcUNQBTG9G3b134XXF/LbC4vBloCaTm5VSXagta+oY3SFKQxPAZXx7X+wcFGjqxDjZXG1e66QnA2JJH4aBDsRfSXmUtD8MblwFYdcCJWz+Ck=

View file

@ -1,19 +0,0 @@
FROM python:3.7
RUN apt-get update \
&& apt-get install -qqy --no-install-recommends \
apt-utils \
ffmpeg \
sox \
libcairo2-dev \
texlive \
texlive-fonts-extra \
texlive-latex-extra \
texlive-latex-recommended \
texlive-science \
tipa \
&& rm -rf /var/lib/apt/lists/*
COPY . /manim
RUN cd /manim \
&& python setup.py sdist \
&& python -m pip install dist/manimlib*
ENTRYPOINT ["/bin/bash"]

View file

@ -1,10 +1,6 @@
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:
MIT License
Copyright (c) 2018 3Blue1Brown LLC
Copyright (c) 2020 3Blue1Brown LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

100
README.md
View file

@ -1,30 +1,19 @@
![logo](logo/cropped.png)
[![Build Status](https://travis-ci.org/3b1b/manim.svg?branch=master)](https://travis-ci.org/3b1b/manim)
[![Documentation](https://img.shields.io/badge/docs-EulerTour-blue.svg)](https://www.eulertour.com/docs)
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://choosealicense.com/licenses/mit/)
[![Manim Subreddit](https://img.shields.io/reddit/subreddit-subscribers/manim.svg?color=ff4301&label=reddit)](https://www.reddit.com/r/manim/)
[![Manim Discord](https://img.shields.io/discord/581738731934056449.svg?label=discord)](https://discord.gg/mMRrZQW)
[![docs](https://github.com/3b1b/manim/workflows/docs/badge.svg)](https://3b1b.github.io/manim/)
Manim is an animation engine for explanatory math videos. It's used to create precise animations programmatically, as seen in the videos at [3Blue1Brown](https://www.3blue1brown.com/).
This repository contains the version of manim used by 3Blue1Brown. There is also a community maintained version at https://github.com/ManimCommunity/manim/.
To get help or to join the development effort, please join the [discord](https://discord.gg/mMRrZQW).
This repository contains the version of manim used by 3Blue1Brown. There is also a community maintained version at https://github.com/ManimCommunity/manim/. To get help or to join the development effort, please join the discord.
## Installation
Manim runs on Python 3.6 or higher version. You can install it from PyPI via pip:
Manim runs on Python 3.8.
```sh
pip3 install manimlib
```
System requirements are [cairo](https://www.cairographics.org), [ffmpeg](https://www.ffmpeg.org), [sox](http://sox.sourceforge.net) (optional, if you want to play the prompt tone after running), [latex](https://www.latex-project.org) (optional, if you want to use LaTeX).
You can now use it via the `manim` command. For example:
```sh
manim my_project.py MyScene
```
System requirements are [FFmpeg](https://ffmpeg.org/), [OpenGL](https://www.opengl.org//), [LaTeX](https://www.latex-project.org) (optional, if you want to use LaTeX)
and [cairo](https://www.cairographics.org/) (optional, if you want to use Text).
For more options, take a look at the [Using manim](#using-manim) sections further below.
@ -34,36 +23,28 @@ If you want to hack on manimlib itself, clone this repository and in that direct
```sh
# Install python requirements
python3 -m pip install -r requirements.txt
pip install -r requirements.txt
# Try it out
python3 ./manim.py example_scenes.py SquareToCircle -pl
python manim.py example_scenes.py OpeningManimExample
```
### Directly (Windows)
1. [Install FFmpeg](https://www.wikihow.com/Install-FFmpeg-on-Windows).
2. [Install Cairo](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo). For most users, ``pycairo1.18.0cp37cp37mwin32.whl`` will do fine (you can download it or other versions from [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo)).
```sh
pip3 install C:\path\to\wheel\pycairo1.18.0cp37cp37mwin32.whl
```
3. Install a LaTeX distribution. [MiKTeX](https://miktex.org/download) is recommended.
4. [Install SoX](https://sourceforge.net/projects/sox/files/sox/).
5. Install the remaining Python packages.
2. Install a LaTeX distribution. [MiKTeX](https://miktex.org/download) is recommended.
3. Install the remaining Python packages.
```sh
git clone https://github.com/3b1b/manim.git
cd manim
pip3 install -r requirements.txt
python3 manim.py example_scenes.py SquareToCircle -pl
pip install -r requirements.txt
python manim.py example_scenes.py OpeningManimExample
```
## Anaconda Install
* Install sox and latex as above.
* Install latex as above.
* Create a conda environment using `conda env create -f environment.yml`
* **WINDOWS ONLY** Install `pyreadline` via `pip install pyreadline`.
### Using `virtualenv` and `virtualenvwrapper`
@ -71,68 +52,41 @@ After installing `virtualenv` and `virtualenvwrapper`
```sh
git clone https://github.com/3b1b/manim.git
mkvirtualenv -a manim -r requirements.txt manim
python3 -m manim example_scenes.py SquareToCircle -pl
python -m manim example_scenes.py OpeningManimExample
```
### 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 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://docs.docker.com)
2. [Install Docker Compose](https://docs.docker.com/compose/install/)
3. Render an animation:
```sh
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`.
![docker diagram](./manim_docker_diagram.png)
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.
## Using manim
Try running the following:
```sh
python3 -m manim example_scenes.py SquareToCircle -pl
python -m manim example_scenes.py OpeningManimExample
```
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.
This should pop up a window playing a simple scene.
Some other useful flags include:
Some useful flags include:
* `-w` to write the scene to a file
* `-o` to write the scene to a file and open the result
* `-s` to skip to the end and just show the final frame.
* `-so` will save the final frame to an image and show it
* `-n <number>` to skip ahead to the `n`'th animation of a scene.
* `-f` to show the file in finder (for OSX).
* `-f` to make the playback window fullscreen
Set `MEDIA_DIR` environment variable to specify where the image and animation files will be written.
Take a look at custom_defaults.yml for further configuration. To add your customization, you can either edit this file, or add another file by the same name "custom_defaults.yml" to whatever directory you are running manim from. For example [this is the one](https://github.com/3b1b/videos/blob/master/custom_defaults.yml) for 3blue1brown videos. There you can specify where videos should be output to, where manim should look for image files and sounds you want to read in, and other defaults regarding style and video quality.
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.
Look through [https://github.com/3b1b/videos](https://github.com/3b1b/videos) 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.
### Documentation
Documentation is in progress at [eulertour.com/docs](https://www.eulertour.com/docs/). And there is also an all-in-one documentation and tutorials maintained by **@manim-kindergarten**: [manim.ml](https://manim.ml/) (in Chinese).
Documentation is in progress at [3b1b.github.io/manim](https://3b1b.github.io/manim/). And there is also a Chinese version maintained by **@manim-kindergarten**: [manim.ml](https://manim.ml/) (in Chinese).
### Walkthrough
Todd Zimmerman put together a [tutorial](https://talkingphysics.wordpress.com/2019/01/08/getting-started-animating-with-manim-and-python-3-7/) on getting started with manim, which has been updated to run on Python 3.7.
[manim-kindergarten](https://github.com/manim-kindergarten/) wrote and collected some useful extra classes and some codes of videos in [manim_sandbox repo](https://github.com/manim-kindergarten/manim_sandbox).
## Contributing
Only accepts pull requests that fixes bugs / fixes typos / improves existing content (for more information, see [#1243](https://github.com/3b1b/manim/issues/1243)). Most pull requests should be directed to the [community version](https://github.com/ManimCommunity/manim/).
Is always welcome. In particular, there is a dire need for tests and documentation.
## License
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.
This project falls under the MIT license.

56
custom_defaults.yml Normal file
View file

@ -0,0 +1,56 @@
directories:
# Set this to true if you want the path to video files
# to match the directory structure of the path to the
# sourcecode generating that video
mirror_module_path: False
# Where should manim output video and image files?
output: ""
# If you want to use images, manim will look to these folders to find them
raster_images: ""
vector_images: ""
# If you want to use sounds, manim will look here to find it.
sounds: ""
# Manim often generates tex_files or other kinds of serialized data
# to keep from having to generate the same thing too many times. By
# default, these will be stored at tempfile.gettempdir(), e.g. this might
# return whatever is at to the TMPDIR environment variable. If you want to
# specify them elsewhere,
temporary_storage: ""
tex:
executable: "latex"
template_file: "tex_template.tex"
intermediate_filetype: "dvi"
text_to_replace: "[tex_expression]"
# # For ctex, use the following configuration
# executable: "xelatex -no-pdf"
# template_file: "ctex_template.tex"
# intermediate_filetype: "xdv"
universal_import_line: "from manimlib.imports import *"
style:
font: "Consolas"
background_color: "#333333"
# Set the position of preview window, you can use directions, e.g. UL/DR/OL/OO/...
# also, you can also specify the position(pixel) of the upper left corner of
# the window on the monitor, e.g. "960,540"
window_position: UR
# If break_into_partial_movies is set to True, then many small
# files will be written corresponding to each Scene.play and
# Scene.wait call, and these files will then be combined
# to form the full scene. Sometimes video-editing is made
# easier when working with the broken up scene, which
# effectively has cuts at all the places you might want.
break_into_partial_movies: False
camera_qualities:
low:
resolution: "854x480"
frame_rate: 15
medium:
resolution: "1280x720"
frame_rate: 30
high:
resolution: "1920x1080"
frame_rate: 30
ultra_high:
resolution: "3840x2160"
frame_rate: 60
default_quality: "high"

View file

@ -1,16 +0,0 @@
version: '3.1'
services:
manim:
# comment this line if you build the image to prevent overwriting the tag
image: eulertour/manim:latest
# uncomment this line to build rather than pull the image
# build: .
entrypoint:
- 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"

View file

@ -1,9 +1,10 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
@ -16,4 +17,4 @@ help:
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

37
docs/example.py Normal file
View file

@ -0,0 +1,37 @@
from manimlib.imports import *
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square = Square()
self.play(ShowCreation(square))
self.wait()
self.play(ReplacementTransform(square, circle))
self.wait()
# Try typing the following lines
# self.play(circle.stretch, 4, {"dim": 0})
# self.play(Rotate(circle, TAU / 4))
# self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25)
# circle.insert_n_curves(10)
# self.play(circle.apply_complex_function, lambda z: z**2)
class SquareToCircleEmbed(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
self.add(circle)
self.wait()
self.play(circle.stretch, 4, {"dim": 0})
self.wait(1.5)
self.play(Rotate(circle, TAU / 4))
self.wait(1.5)
self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25)
self.wait(1.5)
circle.insert_n_curves(10)
self.play(circle.apply_complex_function, lambda z: z**2)
self.wait(2)

View file

@ -1,35 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

4
docs/requirements.txt Normal file
View file

@ -0,0 +1,4 @@
Sphinx==3.0.3
sphinx-copybutton
furo==2020.10.5b9
Jinja2

View file

@ -0,0 +1,293 @@
p.color-text {
font-size: inherit;
font-family: var(--font-stack--monospace);
margin-top: 25px;
color: WHITE;
}
p.color-text-small {
font-size: small;
font-family: var(--font-stack--monospace);
margin-top: 28px;
color: WHITE;
}
.colors {
float: left;
padding: 10px;
border: 10px;
margin: 0;
width: 80px;
height: 80px;
text-align: center;
}
.BLUE_A {
background: #C7E9F1;
color:#C7E9F1;
}
.BLUE_B {
background: #9CDCEB;
color:#9CDCEB;
}
.BLUE_C {
background: #58C4DD;
color:#58C4DD;
}
.BLUE_D {
background: #29ABCA;
color:#29ABCA;
}
.BLUE_E {
background: #1C758A;
color:#1C758A;
}
.TEAL_A {
background: #ACEAD7;
color:#ACEAD7 ;
}
.TEAL_B {
background: #76DDC0;
color: #76DDC0;
}
.TEAL_C {
background: #5CD0B3;
color: #5CD0B3;
}
.TEAL_D {
background: #55C1A7;
color: #55C1A7;
}
.TEAL_E {
background: #49A88F;
color: #49A88F;
}
.GREEN_A {
background: #C9E2AE;
color: #C9E2AE;
}
.GREEN_B {
background: #A6CF8C;
color: #A6CF8C;
}
.GREEN_C {
background: #83C167;
color: #83C167;
}
.GREEN_D {
background: #77B05D;
color: #77B05D;
}
.GREEN_E {
background: #699C52;
color: #699C52;
}
.YELLOW_A {
background: #FFF1B6;
color: #FFF1B6;
}
.YELLOW_B {
background: #FFEA94;
color:#FFEA94 ;
}
.YELLOW_C {
background: #FFFF00;
color: #FFFF00;
}
.YELLOW_D {
background: #F4D345;
color: #F4D345;
}
.YELLOW_E {
background: #E8C11C;
color: #E8C11C;
}
.GOLD_A {
background: #F7C797;
color:#F7C797;
}
.GOLD_B {
background: #F9B775;
color:#F9B775;
}
.GOLD_C {
background: #F0AC5F;
color:#F0AC5F;
}
.GOLD_D {
background: #E1A158;
color:#E1A158;
}
.GOLD_E {
background: #C78D46;
color:#C78D46;
}
.RED_A {
background: #F7A1A3;
color:#F7A1A3;
}
.RED_B {
background: #FF8080;
color:#FF8080;
}
.RED_C {
background: #FC6255;
color:#FC6255;
}
.RED_D {
background: #E65A4C;
color:#E65A4C;
}
.RED_E {
background: #CF5044;
color:#CF5044;
}
.MAROON_A {
background: #ECABC1;
color: #ECABC1;
}
.MAROON_B {
background: #EC92AB;
color: #EC92AB;
}
.MAROON_C {
background: #C55F73;
color: #C55F73;
}
.MAROON_D {
background: #A24D61;
color: #A24D61;
}
.MAROON_E {
background: #94424F;
color: #94424F;
}
.PURPLE_A {
background: #CAA3E8;
color: #CAA3E8;
}
.PURPLE_B {
background: #B189C6;
color: #B189C6;
}
.PURPLE_C {
background: #9A72AC;
color: #9A72AC;
}
.PURPLE_D {
background: #715582;
color: #715582;
}
.PURPLE_E {
background: #644172;
color: #644172;
}
.GREY_A {
background: #DDDDDD;
color: #DDDDDD;
}
.GREY_B {
background: #BBBBBB;
color: #BBBBBB;
}
.GREY_C {
background: #888888;
color: #888888;
}
.GREY_D {
background: #444444;
color: #444444;
}
.GREY_E {
background: #222222;
color: #222222;
}
.WHITE {
background: #FFFFFF;
color: #FFFFFF;
}
.BLACK {
background: #000000;
color: #000000;
}
.GREY_BROWN {
background: #736357;
color: #736357;
}
.DARK_BROWN {
background: #8B4513;
color: #8B4513;
}
.LIGHT_BROWN {
background: #CD853F;
color: #CD853F;
}
.PINK {
background: #D147BD;
color: #D147BD;
}
.LIGHT_PINK {
background: #DC75CD;
color: #DC75CD;
}
.GREEN_SCREEN {
background: #00FF00;
color: #00FF00;
}
.ORANGE {
background: #FF862F;
color: #FF862F;
}

View file

@ -0,0 +1,62 @@
p {
font-size: initial;
}
span.caption-text {
font-size: larger;
}
span.pre {
font-size: initial;
}
.highlight-python.notranslate {
margin-top: 0em;
}
.manim-video {
width: 99.9%;
padding: 8px 0;
outline: 0;
}
.manim-example {
background-color: #333333;
margin-bottom: 10px;
box-shadow: 2px 2px 4px #ddd;
}
.manim-example .manim-video {
padding: 0;
}
.manim-example img {
margin-bottom: 0;
}
h5.example-header {
font-size: 18px;
font-weight: bold;
padding: 8px 16px;
color: white;
margin: 0;
font-family: inherit;
text-transform: none;
margin-top: -0.4em;
margin-bottom: -0.2em;
}
.manim-example .highlight {
background-color: #fafafa;
border: 2px solid #333333;
padding: 8px 8px 10px 8px;
font-size: large;
margin: 0;
}
.manim-example .highlight pre {
background-color: inherit;
border-left: none;
margin: 0;
padding: 0 6px 0 6px;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

View file

@ -1,11 +0,0 @@
About
=====
Animating technical concepts is traditionally pretty tedious, since it can be
difficult to make the animations precise enough to convey them accurately.
``Manim`` uses Python to generate animations programmatically, which makes it
possible to specify exactly how each one should run.
This project is still very much a work in progress, but I hope that the
information here will make it easier for newcomers to get started using
``Manim``.

View file

@ -1,210 +0,0 @@
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`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View file

@ -1,53 +1,40 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
sys.path.insert(0, os.path.abspath("."))
sys.path.insert(0, os.path.abspath('../../'))
# -- Project information -----------------------------------------------------
project = 'manim'
copyright = '- This document has been placed in the public domain.'
author = 'TonyCrane'
project = 'Manim'
copyright = '2019, EulerTour'
author = 'EulerTour'
release = ''
# -- 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
# ones.
extensions = [
'sphinx.ext.todo',
'sphinx.ext.githubpages',
'sphinx.ext.mathjax',
'sphinx.ext.intersphinx',
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.napoleon',
'sphinx_copybutton',
'manim_example_ext'
]
# Add any paths that contain templates here, relative to this directory.
autoclass_content = 'both'
mathjax_path = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
pygments_style = 'default'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
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 = ['assets']
html_static_path = ["_static"]
html_css_files = ["custom.css", "colors.css"]
html_theme = 'furo' # pip install furo==2020.10.5b9
html_favicon = '_static/icon.png'
html_logo = '../../logo/transparent_graph.png'
html_theme_options = {
"sidebar_hide_name": True,
}

View file

@ -1,94 +0,0 @@
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

View file

@ -1,178 +0,0 @@
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>

View file

@ -0,0 +1,31 @@
About
=====
About Manim
-----------
Manim is an animation engine for explanatory math videos.
You can use it to make math videos (or other fields) like 3Blue1Brown.
There are mainly two versions here:
- `3b1b/manim <https://github.com/3b1b/manim>`_ : Maintained by Grant Sanderson of 3Blue1Brown.
Using OpenGL and its GLSL language to use GPU for rendering. It has higher efficiency,
faster rendering speed, and supports real-time rendering and interaction.
- `ManimCommunity/manim <https://github.com/ManimCommunity/manim>`_ : Maintained by Manim Community Dev Team.
Using cairo to use CPU for rendering. There is better documentation and
a more open contribution community.
About this documentation
------------------------
This documentation is based on the version in `3b1b/manim <https://github.com/3b1b/manim>`_.
Created by `TonyCrane <https://github.com/TonyCrane>`_ ("鹤翔万里" in Chinese) and in production.
Among them, the ``manim_example_ext`` extension for Sphinx refers to
`the documentation of ManimCommunity <https://docs.manim.community/>`_.
If you want to contribute to manim or this document, please see: :doc:`contributing`

View file

@ -0,0 +1,4 @@
Changelog
=========
No changes now.

View file

@ -0,0 +1,59 @@
Contributing
============
Accept any contribution you make :)
- **Contribute to the manim source code**:
Please fork to your own repository and make changes, submit a pull request, and fill in
the motivation for the change following the instructions in the template. We will check
your pull request in detail (this usually takes a while, please be patient)
- **Contribute to the documentation**:
Also submit a pull request and write down the main changes.
- **If you find a bug in the code**:
Please open an issue and fill in the found problem and your environment according
to the template. (But please note that if you think this problem is just a problem
of yourself, rather than a problem of source code, it is recommended that you ask a
question in the `Q&A category <https://github.com/3b1b/manim/discussions/categories/q-a>`_
of the discussion page)
- **You are welcome to share the content you made with manim**:
Post it in the `show and tell category <https://github.com/3b1b/manim/discussions/categories/show-and-tell>`_
of the discussion page.
- **You are also welcome to share some of your suggestions and ideas**:
Post them in the `ideas category <https://github.com/3b1b/manim/discussions/categories/ideas>`_
of the discussion page.
How to build this documentation
-------------------------------
- Clone the 3b1b/manim repository
.. code-block:: sh
git clone https://github.com/3b1b/manim.git
# or your own repo
# git clone https://github.com/<your user name>/manim.git
cd manim
- Install python package dependencies
.. code-block:: sh
pip install -r docs/requirements.txt
- Go to the ``docs/`` folder and build
.. code-block:: sh
cd docs/
make html
- The output document is located in ``docs/build/html/``

View file

@ -0,0 +1,2 @@
Animation (TODO)
================

View file

@ -0,0 +1,2 @@
Camera (TODO)
=============

View file

@ -0,0 +1,192 @@
constants
=========
The ``constants.py`` in the ``manimlib`` folder defines the constants
needed when running manim. Some constants are not explained here because
they are only used inside manim.
Frame and pixel shape
---------------------
.. code-block:: python
ASPECT_RATIO = 16.0 / 9.0
FRAME_HEIGHT = 8.0
FRAME_WIDTH = FRAME_HEIGHT * ASPECT_RATIO
FRAME_Y_RADIUS = FRAME_HEIGHT / 2
FRAME_X_RADIUS = FRAME_WIDTH / 2
DEFAULT_PIXEL_HEIGHT = 1080
DEFAULT_PIXEL_WIDTH = 1920
DEFAULT_FRAME_RATE = 30
Buffs
-----
.. code-block:: python
SMALL_BUFF = 0.1
MED_SMALL_BUFF = 0.25
MED_LARGE_BUFF = 0.5
LARGE_BUFF = 1
DEFAULT_MOBJECT_TO_EDGE_BUFFER = MED_LARGE_BUFF # Distance between object and edge
DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = MED_SMALL_BUFF # Distance between objects
Run times
---------
.. code-block:: python
DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0
DEFAULT_WAIT_TIME = 1.0
Coordinates
-----------
manim uses three-dimensional coordinates and uses the type of ``ndarray``
.. code-block:: python
ORIGIN = np.array((0., 0., 0.))
UP = np.array((0., 1., 0.))
DOWN = np.array((0., -1., 0.))
RIGHT = np.array((1., 0., 0.))
LEFT = np.array((-1., 0., 0.))
IN = np.array((0., 0., -1.))
OUT = np.array((0., 0., 1.))
X_AXIS = np.array((1., 0., 0.))
Y_AXIS = np.array((0., 1., 0.))
Z_AXIS = np.array((0., 0., 1.))
# Useful abbreviations for diagonals
UL = UP + LEFT
UR = UP + RIGHT
DL = DOWN + LEFT
DR = DOWN + RIGHT
TOP = FRAME_Y_RADIUS * UP
BOTTOM = FRAME_Y_RADIUS * DOWN
LEFT_SIDE = FRAME_X_RADIUS * LEFT
RIGHT_SIDE = FRAME_X_RADIUS * RIGHT
Mathematical constant
---------------------
.. code-block:: python
PI = np.pi
TAU = 2 * PI
DEGREES = TAU / 360
Text
----
.. code-block:: python
START_X = 30
START_Y = 20
NORMAL = "NORMAL"
ITALIC = "ITALIC"
OBLIQUE = "OBLIQUE"
BOLD = "BOLD"
Stroke width
------------
.. code-block:: python
DEFAULT_STROKE_WIDTH = 4
Colours
-------
Here are the preview of default colours. (Modified from
`elteoremadebeethoven <https://elteoremadebeethoven.github.io/manim_3feb_docs.github.io/html/_static/colors/colors.html>`_)
.. raw:: html
<div style="float: left;">
<h3>BLUE</h3>
<div class="colors BLUE_E"><p class="color-text">BLUE_E</p></div>
<div class="colors BLUE_D"><p class="color-text">BLUE_D</p></div>
<div class="colors BLUE_C"><p class="color-text">BLUE_C</p></div>
<div class="colors BLUE_B"><p class="color-text">BLUE_B</p></div>
<div class="colors BLUE_A"><p class="color-text">BLUE_A</p></div>
</div>
<div style="float: left;">
<h3>TEAL</h3>
<div class="colors TEAL_E"><p class="color-text">TEAL_E</p></div>
<div class="colors TEAL_D"><p class="color-text">TEAL_D</p></div>
<div class="colors TEAL_C"><p class="color-text">TEAL_C</p></div>
<div class="colors TEAL_B"><p class="color-text">TEAL_B</p></div>
<div class="colors TEAL_A"><p class="color-text">TEAL_A</p></div>
</div>
<div style="float: left;">
<h3>GREEN</h3>
<div class="colors GREEN_E"><p class="color-text">GREEN_E</p></div>
<div class="colors GREEN_D"><p class="color-text">GREEN_D</p></div>
<div class="colors GREEN_C"><p class="color-text">GREEN_C</p></div>
<div class="colors GREEN_B"><p class="color-text">GREEN_B</p></div>
<div class="colors GREEN_A"><p class="color-text">GREEN_A</p></div>
</div>
<div style="float: left;">
<h3>YELLOW</h3>
<div class="colors YELLOW_E"><p class="color-text">YELLOW_E</p></div>
<div class="colors YELLOW_D"><p class="color-text">YELLOW_D</p></div>
<div class="colors YELLOW_C"><p class="color-text">YELLOW_C</p></div>
<div class="colors YELLOW_B"><p class="color-text">YELLOW_B</p></div>
<div class="colors YELLOW_A"><p class="color-text">YELLOW_A</p></div>
</div>
<div style="float: left;">
<h3>GOLD</h3>
<div class="colors GOLD_E"><p class="color-text">GOLD_E</p></div>
<div class="colors GOLD_D"><p class="color-text">GOLD_D</p></div>
<div class="colors GOLD_C"><p class="color-text">GOLD_C</p></div>
<div class="colors GOLD_B"><p class="color-text">GOLD_B</p></div>
<div class="colors GOLD_A"><p class="color-text">GOLD_A</p></div>
</div>
<div style="float: left;">
<h3>RED</h3>
<div class="colors RED_E"><p class="color-text">RED_E</p></div>
<div class="colors RED_D"><p class="color-text">RED_D</p></div>
<div class="colors RED_C"><p class="color-text">RED_C</p></div>
<div class="colors RED_B"><p class="color-text">RED_B</p></div>
<div class="colors RED_A"><p class="color-text">RED_A</p></div>
</div>
<div style="float: left;">
<h3>MAROON</h3>
<div class="colors MAROON_E"><p class="color-text">MAROON_E</p></div>
<div class="colors MAROON_D"><p class="color-text">MAROON_D</p></div>
<div class="colors MAROON_C"><p class="color-text">MAROON_C</p></div>
<div class="colors MAROON_B"><p class="color-text">MAROON_B</p></div>
<div class="colors MAROON_A"><p class="color-text">MAROON_A</p></div>
</div>
<div style="float: left;">
<h3>PURPLE</h3>
<div class="colors PURPLE_E"><p class="color-text">PURPLE_E</p></div>
<div class="colors PURPLE_D"><p class="color-text">PURPLE_D</p></div>
<div class="colors PURPLE_C"><p class="color-text">PURPLE_C</p></div>
<div class="colors PURPLE_B"><p class="color-text">PURPLE_B</p></div>
<div class="colors PURPLE_A"><p class="color-text">PURPLE_A</p></div>
</div>
<div style="float: left;">
<h3>GREY</h3>
<div class="colors GREY_E"><p class="color-text">GREY_E</p></div>
<div class="colors GREY_D"><p class="color-text">GREY_D</p></div>
<div class="colors GREY_C"><p class="color-text">GREY_C</p></div>
<div class="colors GREY_B"><p class="color-text">GREY_B</p></div>
<div class="colors GREY_A"><p class="color-text">GREY_A</p></div>
</div>
<div style="float: left;">
<h3>Others</h3>
<div class="colors WHITE"><p class="color-text" style="color: BLACK">WHITE</p></div>
<div class="colors BLACK"><p class="color-text">BLACK</p></div>
<div class="colors GREY_BROWN"><p class="color-text-small">GREY_BROWN</p></div>
<div class="colors DARK_BROWN"><p class="color-text-small">DARK_BROWN</p></div>
<div class="colors LIGHT_BROWN"><p class="color-text-small">LIGHT_BROWN</p></div>
<div class="colors PINK"><p class="color-text">PINK</p></div>
<div class="colors LIGHT_PINK"><p class="color-text-small">LIGHT_PINK</p></div>
<div class="colors GREEN_SCREEN"><p class="color-text-small">GREEN_SCREEN</p></div>
<div class="colors ORANGE"><p class="color-text">ORANGE</p></div>
</div>

View file

@ -0,0 +1,139 @@
custom_default
==============
``directories``
---------------
- ``mirror_module_path``
(``True`` or ``False``) Whether to create a folder named the name of the
running file under the ``output`` path, and save the output (``images/``
or ``videos/``) in it.
- ``output``
Output file path, the videos will be saved in the ``videos/`` folder under it,
and the pictures will be saved in the ``images/`` folder under it.
For example, if you set ``output`` to ``"/.../manim/output"`` and
``mirror_module_path`` to ``False``, then you exported ``Scene1`` in the code
file and saved the last frame, then the final directory structure will be like:
.. code-block:: text
:emphasize-lines: 8, 10
manim/
├── manimlib/
│ ├── animation/
│ ├── ...
│ └── window.py
├── output/
│ ├── images
│ │ └── Scene1.png
│ └── videos
│ └── Scene1.mp4
├── code.py
├── custom_default.yml
└── manim.py
But if you set ``mirror_module_path`` to ``True``, the directory structure will be:
.. code-block:: text
:emphasize-lines: 7
manim/
├── manimlib/
│ ├── animation/
│ ├── ...
│ └── window.py
├── output/
│ └── code/
│ ├── images
│ │ └── Scene1.png
│ └── videos
│ └── Scene1.mp4
├── code.py
├── custom_default.yml
└── manim.py
- ``raster_images``
The directory for storing raster images to be used in the code (including
``.jpg``, ``.png`` and ``.gif``), which will be read by ``ImageMobject``.
- ``vector_images``
The directory for storing vector images to be used in the code (including
``.svg`` and ``.xdv``), which will be read by ``SVGMobject``.
- ``sounds``
The directory for storing sound files to be used in ``Scene.add_sound()`` (
including ``.wav`` and ``.mp3``).
- ``temporary_storage``
The directory for storing temporarily generated cache files, including
``Tex`` cache, ``Text`` cache and storage of object points.
``tex``
-------
- ``executable``
The executable program used to compile LaTeX (``latex`` or ``xelatex -no-pdf``
is recommended)
- ``template_file``
LaTeX template used, in ``manimlib/tex_templates``
- ``intermediate_filetype``
The type of intermediate vector file generated after compilation (``dvi`` if
``latex`` is used, ``xdv`` if ``xelatex`` is used)
- ``text_to_replace``
The text to be replaced in the template (needn't to change)
``universal_import_line``
-------------------------
Import line that need to execute when entering interactive mode directly.
``style``
---------
- ``font``
Default font of Text
- ``background_color``
Default background color
``window_position``
-------------------
The relative position of the playback window on the display (two characters,
the first character means upper(U) / middle(O) / lower(D), the second character
means left(L) / middle(O) / right(R)).
``break_into_partial_movies``
-----------------------------
If this is set to ``True``, then many small files will be written corresponding
to each ``Scene.play`` and ``Scene.wait`` call, and these files will then be combined
to form the full scene.
Sometimes video-editing is made easier when working with the broken up scene, which
effectively has cuts at all the places you might want.
``camera_qualities``
--------------------
Export quality
- ``low``
Low quality (default is 480p15)
- ``medium``
Medium quality (default is 720p30)
- ``high``
High quality (default is 1080p30)
- ``ultra_high``
Ultra high quality (default is 4K60)
- ``default_quality``
Default quality (one of the above four)

View file

@ -0,0 +1,2 @@
Mobject (TODO)
==============

View file

@ -0,0 +1,2 @@
Scene (TODO)
============

View file

@ -0,0 +1,2 @@
Shaders (TODO)
==============

View file

@ -0,0 +1,2 @@
Utils (TODO)
============

View file

@ -1,4 +0,0 @@
Animating Mobjects
==================
Learn about animations.

View file

@ -0,0 +1,104 @@
CONFIG dictionary
=================
What's CONFIG
-------------
``CONFIG`` dictionary is a feature of manim, which facilitates the inheritance
and modification of parameters between parent and child classes.
| ``CONFIG`` dictionary 's processing is in ``manimlib/utils/config_ops.py``
| It can convert the key-value pairs in the ``CONFIG`` dictionary into class attributes and values
Generally, the first line of the ``.__init__()`` method in some basic class (``Mobject``, ``Animation``,
etc.) will call this function ``digest_config(self, kwargs)`` to convert both
the ``CONFIG`` dictionary and ``kwargs`` into attributes. Then it can be accessed
directly through ``self.``, which simplifies the handling of inheritance between classes.
**An example**:
There are many class inheritance relationships in ``manimlib/mobject/geometry.py``
.. code-block:: python
# Line 279
class Circle(Arc):
CONFIG = {
"color": RED,
"close_new_points": True,
"anchors_span_full_range": False
}
.. code-block:: python
# Line 304
class Dot(Circle):
CONFIG = {
"radius": DEFAULT_DOT_RADIUS,
"stroke_width": 0,
"fill_opacity": 1.0,
"color": WHITE
}
The ``Circle`` class uses the key-value pair ``"color": RED`` in the ``CONFIG``
dictionary to add the attribute ``self.color``.
At the same time, the ``Dot`` class also contains the key ``color`` in the
``CONFIG`` dictionary, but the value is different. At this time, the priority will
modify the attribute ``self.color`` to ``WHITE``.
CONFIG nesting
--------------
The ``CONFIG`` dictionary supports nesting, that is, the value of the key is also
a dictionary, for example:
.. code-block:: python
class Camera(object):
CONFIG = {
# configs
}
.. code-block:: python
class Scene(object):
CONFIG = {
"window_config": {},
"camera_class": Camera,
"camera_config": {},
"file_writer_config": {},
# other configs
}
def __init__(self, **kwargs):
digest_config(self, kwargs)
# some lines
self.camera = self.camera_class(**self.camera_config)
The ``CONFIG`` dictionary of the ``Camera`` class contains many key-value pairs,
and this class needs to be instantiated in the ``Scene`` class. For more convenient
control, there is a special key-value pair in the Scene class ``"camera_config": {}``,
Its value is a dictionary, passed in as ``kwargs`` when initializing the ``Camera`` class
to modify the value of the properties of the ``Camera`` class.
So the nesting of the ``CONFIG`` dictionary **essentially** passes in the value as ``kwargs``.
Common usage
------------
When writing a class by yourself, you can add attributes or modify the attributes
of the parent class through ``CONFIG``.
The most commonly used is to modify the properties of the camera when writing a ``Scene``:
.. code-block:: python
class YourScene(Scene):
CONFIG = {
"camera_config": {
"background_color": WHITE,
},
}
For example, the above dictionary will change the background color to white, etc.

View file

@ -0,0 +1,86 @@
CLI flags and configuration
===========================
Command Line Interface
----------------------
To run manim, you need to enter the directory at the same level as ``manim.py``
and enter the command in the following format into terminal:
.. code-block:: sh
python manim.py <code>.py <Scene> <flags>
- ``<code>.py`` : The python file you wrote. Needs to be at the same level as ``manim.py``, otherwise you need to use an absolute path or a relative path.
- ``<Scene>`` : The scene you want to render here. If it is not written or written incorrectly, it will list all for you to choose. And if there is only one ``Scene`` in the file, this class will be rendered directly.
- ``<flags>`` : CLI flags.
Some useful flags
^^^^^^^^^^^^^^^^^
- ``-w`` to write the scene to a file.
- ``-o`` to write the scene to a file and open the result.
- ``-s`` to skip to the end and just show the final frame.
- ``-so`` will save the final frame to an image and show it.
- ``-n <number>`` to skip ahead to the ``n``\ th animation of a scene.
- ``-f`` to make the playback window fullscreen.
All supported flags
^^^^^^^^^^^^^^^^^^^
========================================================== ====== =================================================================================================================================================================================================
flag abbr function
========================================================== ====== =================================================================================================================================================================================================
``--help`` ``-h`` Show the help message and exit
``--write_file`` ``-w`` Render the scene as a movie file
``--skip_animations`` ``-s`` Skip to the last frame
``--low_quality`` ``-l`` Render at a low quality (for faster rendering)
``--medium_quality`` ``-m`` Render at a medium quality
``--hd`` Render at a 1080p quality
``--uhd`` Render at a 4k quality
``--full_screen`` ``-f`` Show window in full screen
``--save_pngs`` ``-g`` Save each frame as a png
``--save_as_gif`` ``-i`` Save the video as gif
``--transparent`` ``-t`` Render to a movie file with an alpha channel
``--quiet`` ``-q``
``--write_all`` ``-a`` Write all the scenes from a file
``--open`` ``-o`` Automatically open the saved file once its done
``--finder`` Show the output file in finder
``--file_name FILE_NAME`` Name for the movie or image file
``--start_at_animation_number START_AT_ANIMATION_NUMBER`` ``-n`` Start rendering not from the first animation, but from another, specified by its index. If you passin two comma separated values, e.g. "3,6", it will end the rendering at the second value.
``--resolution RESOLUTION`` ``-r`` Resolution, passed as "WxH", e.g. "1920x1080"
``--frame_rate FRAME_RATE`` Frame rate, as an integer
``--color COLOR`` ``-c`` Background color
``--leave_progress_bars`` Leave progress bars displayed in terminal
``--video_dir VIDEO_DIR`` directory to write video
========================================================== ====== =================================================================================================================================================================================================
custom_default
--------------
In order to perform more configuration (about directories, etc.) and permanently
change the default value (you don't have to add flags to the command every time),
you can modify ``custom_default.yml``. The meaning of each option is in
page :doc:`../documentation/custom_default`.
You can also use different ``custom_default.yml`` for different directories, such as
following the directory structure:
.. code-block:: text
manim/
├── manimlib/
│ ├── animation/
│ ├── ...
│ └── window.py
├── project/
│ ├── code.py
│ └── custom_default.yml
├── custom_default.yml
└── manim.py
When you enter the ``project/`` folder and run ``python ../manim.py code.py <Scene>``,
it will overwrite ``manim/custom_default.yml`` with ``custom_default.yml``
in the ``project`` folder.

View file

@ -0,0 +1,531 @@
Example Scenes
==============
After understanding the previous knowledge, we can understand more scenes.
Many example scenes are given in ``example_scenes.py``, let's start with
the simplest and one by one.
SquareToCircle
--------------
.. manim-example:: SquareToCircle
:media: ../_static/example_scenes/SquareToCircle.mp4
from manimlib.imports import *
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square = Square()
self.play(ShowCreation(square))
self.wait()
self.play(ReplacementTransform(square, circle))
self.wait()
This scene is what we wrote in :doc:`quickstart`.
No more explanation here
AnimatingMethods
----------------
.. manim-example:: AnimatingMethods
:media: ../_static/example_scenes/AnimatingMethods.mp4
class AnimatingMethods(Scene):
def construct(self):
grid = Tex(r"\pi").get_grid(10, 10, height=4)
self.add(grid)
# If you pass in a mobject method to the scene's "play" function,
# it will apply an animation interpolating between the mobject's
# initial state and whatever happens when you apply that method.
# For example, calling grid.shift(2 * LEFT) would shift it two units
# to the left, but the following line animates that motion.
self.play(grid.shift, 2 * LEFT)
# The same applies for any method, including those setting colors.
self.play(grid.set_submobject_colors_by_gradient, BLUE, GREEN)
self.play(grid.set_height, TAU - MED_SMALL_BUFF)
self.wait()
# The method Mobject.apply_complex_function lets you apply arbitrary
# complex functions, treating the points defining the mobject as
# complex numbers.
self.play(grid.apply_complex_function, np.exp, run_time=5)
self.wait()
# Even more generally, you could apply Mobject.apply_function,
# which takes in functions form R^3 to R^3
self.play(
grid.apply_function,
lambda p: [
p[0] + 0.5 * math.sin(p[1]),
p[1] + 0.5 * math.sin(p[0]),
p[2]
],
run_time=5,
)
self.wait()
The new usage in this scene is ``.get_grid()`` and ``self.play(mob.method, args)``.
- ``.get_grid()`` method will return a new mobject containing multiple copies of this one arranged in a grid.
- ``self.play(mob.method, args)`` animate the method, and the details are in the comments above.
TextExample
-----------
.. manim-example:: TextExample
:media: ../_static/example_scenes/TextExample.mp4
class TextExample(Scene):
def construct(self):
text = Text("Here is a text", font="Consolas", font_size=90)
difference = Text(
"""
The most important difference between Text and TexText is that\n
you can change the font more easily, but can't use the LaTeX grammar
""",
font="Arial", font_size=24,
t2c={"Text": BLUE, "TexText": BLUE, "LaTeX": ORANGE}
)
VGroup(text, difference).arrange(DOWN, buff=1)
self.play(Write(text))
self.play(FadeIn(difference, UP))
self.wait(3)
fonts = Text(
"And you can also set the font according to different words",
font="Arial",
t2f={"font": "Consolas", "words": "Consolas"},
t2c={"font": BLUE, "words": GREEN}
)
slant = Text(
"And the same as slant and weight",
font="Consolas",
t2s={"slant": ITALIC},
t2w={"weight": BOLD},
t2c={"slant": ORANGE, "weight": RED}
)
VGroup(fonts, slant).arrange(DOWN, buff=0.8)
self.play(FadeOut(text), FadeOut(difference, shift=DOWN))
self.play(Write(fonts))
self.wait()
self.play(Write(slant))
self.wait()
The new classes in this scene are ``Text``, ``VGroup``, ``Write``, ``FadeIn`` and ``FadeOut``.
- ``Text`` can create text, define fonts, etc. The usage ais clearly reflected in the above examples.
- ``VGroup`` can put multiple ``VMobject`` together as a whole. In the example, the ``.arrange()`` method is called to arrange the sub-mobjects in sequence downward (``DOWN``), and the spacing is ``buff``.
- ``Write`` is an animation that shows similar writing effects.
- ``FadeIn`` fades the object in, the second parameter indicates the direction of the fade in.
- ``FadeOut`` fades out the object, the second parameter indicates the direction of the fade out.
TexTransformExample
-------------------
.. manim-example:: TexTransformExample
:media: ../_static/example_scenes/TexTransformExample.mp4
class TexTransformExample(Scene):
def construct(self):
to_isolate = ["B", "C", "=", "(", ")"]
lines = VGroup(
# Surrounding substrings with double braces
# will ensure that those parts are separated
# out in the Tex. For example, here the
# Tex will have 5 submobjects, corresponding
# to the strings [A^2, +, B^2, =, C^2]
Tex("{{A^2}} + {{B^2}} = {{C^2}}"),
Tex("{{A^2}} = {{C^2}} - {{B^2}}"),
# Alternatively, you can pass in the keyword argument
# "isolate" with a list of strings that should be out as
# their own submobject. So both lines below are equivalent
# to what you'd get by wrapping every instance of "B", "C"
# "=", "(" and ")" with double braces
Tex("{{A^2}} = (C + B)(C - B)", isolate=to_isolate),
Tex("A = \\sqrt{(C + B)(C - B)}", isolate=to_isolate)
)
lines.arrange(DOWN, buff=LARGE_BUFF)
for line in lines:
line.set_color_by_tex_to_color_map({
"A": BLUE,
"B": TEAL,
"C": GREEN,
})
play_kw = {"run_time": 2}
self.add(lines[0])
# The animation TransformMatchingTex will line up parts
# of the source and target which have matching tex strings.
# Here, giving it a little path_arc makes each part sort of
# rotate into their final positions, which feels appropriate
# for the idea of rearranging an equation
self.play(
TransformMatchingTex(
lines[0].copy(), lines[1],
path_arc=90 * DEGREES,
),
**play_kw
)
self.wait()
# Now, we could try this again on the next line...
self.play(
TransformMatchingTex(lines[1].copy(), lines[2]),
**play_kw
)
self.wait()
# ...and this looks nice enough, but since there's no tex
# in lines[2] which matches "C^2" or "B^2", those terms fade
# out to nothing while the C and B terms fade in from nothing.
# If, however, we want the C^2 to go to C, and B^2 to go to B,
# we can specify that with a key map.
self.play(FadeOut(lines[2]))
self.play(
TransformMatchingTex(
lines[1].copy(), lines[2],
key_map={
"C^2": "C",
"B^2": "B",
}
),
**play_kw
)
self.wait()
# And to finish off, a simple TransformMatchingShapes would work
# just fine. But perhaps we want that exponent on A^2 to transform into
# the square root symbol. At the moment, lines[2] treats the expression
# A^2 as a unit, so we might create a new version of the same line which
# separates out just the A. This way, when TransformMatchingTex lines up
# all matching parts, the only mismatch will be between the "^2" from
# new_line2 and the "\sqrt" from the final line. By passing in,
# transform_mismatches=True, it will transform this "^2" part into
# the "\sqrt" part.
new_line2 = Tex("{{A}}^2 = (C + B)(C - B)", isolate=to_isolate)
new_line2.replace(lines[2])
new_line2.match_style(lines[2])
self.play(
TransformMatchingTex(
new_line2, lines[3],
transform_mismatches=True,
),
**play_kw
)
self.wait(3)
self.play(FadeOut(lines, RIGHT))
# Alternatively, if you don't want to think about breaking up
# the tex strings deliberately, you can TransformMatchingShapes,
# which will try to line up all pieces of a source mobject with
# those of a target, regardless of the submobject hierarchy in
# each one, according to whether those pieces have the same
# shape (as best it can).
source = Text("the morse code", height=1)
target = Text("here come dots", height=1)
self.play(Write(source))
self.wait()
kw = {"run_time": 3, "path_arc": PI / 2}
self.play(TransformMatchingShapes(source, target, **kw))
self.wait()
self.play(TransformMatchingShapes(target, source, **kw))
self.wait()
The new classes in this scene are ``Tex``, ``TexText``, ``TransformMatchingTex``
and ``TransformMatchingShapes``.
- ``Tex`` uses LaTeX to create mathematical formulas.
- ``TexText`` uses LaTeX to create text.
- ``TransformMatchingTeX`` automatically transforms sub-objects according to the similarities and differences of tex in ``Tex``.
- ``TransformMatchingShapes`` automatically transform sub-objects directly based on the similarities and differences of the object point sets.
UpdatersExample
---------------
.. manim-example:: UpdatersExample
:media: ../_static/example_scenes/UpdatersExample.mp4
class UpdatersExample(Scene):
def construct(self):
square = Square()
square.set_fill(BLUE_E, 1)
# On all all frames, the constructor Brace(square, UP) will
# be called, and the mobject brace will set its data to match
# that of the newly constructed object
brace = always_redraw(Brace, square, UP)
text, number = label = VGroup(
Text("Width = "),
DecimalNumber(
0,
show_ellipsis=True,
num_decimal_places=2,
include_sign=True,
)
)
label.arrange(RIGHT)
# This ensures that the method deicmal.next_to(square)
# is called on every frame
always(label.next_to, brace, UP)
# You could also write the following equivalent line
# label.add_updater(lambda m: m.next_to(brace, UP))
# If the argument itself might change, you can use f_always,
# for which the arguments following the initial Mobject method
# should be functions returning arguments to that method.
# The following line ensures thst decimal.set_value(square.get_y())
# is called every frame
f_always(number.set_value, square.get_width)
# You could also write the following equivalent line
# number.add_updater(lambda m: m.set_value(square.get_width()))
self.add(square, brace, label)
# Notice that the brace and label track with the square
self.play(
square.scale, 2,
rate_func=there_and_back,
run_time=2,
)
self.wait()
self.play(
square.set_width, 5, {"stretch": True},
run_time=3,
)
self.wait()
self.play(
square.set_width, 2,
run_time=3
)
self.wait()
# In general, you can alway call Mobject.add_updater, and pass in
# a function that you want to be called on every frame. The function
# should take in either one argument, the mobject, or two arguments,
# the mobject and the amount of time since the last frame.
now = self.time
w0 = square.get_width()
square.add_updater(
lambda m: m.set_width(w0 * math.cos(self.time - now))
)
self.wait(4 * PI)
The new classes and usage in this scene are ``always_redraw()``, ``DecimalNumber``, ``.to_edge()``,
``.center()``, ``always()``, ``f_always()``, ``.set_y()`` and ``.add_updater()``.
- ``always_redraw()`` function create a new mobject every frame.
- ``DecimalNumber`` is a variable number, speed it up by breaking it into ``Text`` characters.
- ``.to_edge()`` means to place the object on the edge of the screen.
- ``.center()`` means to place the object in the center of the screen.
- ``always(f, x)`` means that a certain function (``f(x)``) is executed every frame.
- ``f_always(f, g)`` is similar to ``always``, executed ``f(g())`` every frame.
- ``.set_y()`` means to set the ordinate of the object on the screen.
- ``.add_updater()`` sets an update function for the object. For example: ``mob1.add_updater(lambda mob: mob.next_to(mob2))`` means ``mob1.next_to(mob2)`` is executed every frame.
SurfaceExample
--------------
.. manim-example:: SurfaceExample
:media: ../_static/example_scenes/SurfaceExample.mp4
class SurfaceExample(Scene):
CONFIG = {
"camera_class": ThreeDCamera,
}
def construct(self):
surface_text = Text("For 3d scenes, try using surfaces")
surface_text.fix_in_frame()
surface_text.to_edge(UP)
self.add(surface_text)
self.wait(0.1)
torus1 = Torus(r1=1, r2=1)
torus2 = Torus(r1=3, r2=1)
sphere = Sphere(radius=3, resolution=torus1.resolution)
# You can texture a surface with up to two images, which will
# be interpreted as the side towards the light, and away from
# the light. These can be either urls, or paths to a local file
# in whatever you've set as the image directory in
# the custom_defaults.yml file
# day_texture = "EarthTextureMap"
# night_texture = "NightEarthTextureMap"
day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
surfaces = [
TexturedSurface(surface, day_texture, night_texture)
for surface in [sphere, torus1, torus2]
]
for mob in surfaces:
mob.shift(IN)
mob.mesh = SurfaceMesh(mob)
mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
# Set perspective
frame = self.camera.frame
frame.set_euler_angles(
theta=-30 * DEGREES,
phi=70 * DEGREES,
)
surface = surfaces[0]
self.play(
FadeIn(surface),
ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3),
)
for mob in surfaces:
mob.add(mob.mesh)
surface.save_state()
self.play(Rotate(surface, PI / 2), run_time=2)
for mob in surfaces[1:]:
mob.rotate(PI / 2)
self.play(
Transform(surface, surfaces[1]),
run_time=3
)
self.play(
Transform(surface, surfaces[2]),
# Move camera frame during the transition
frame.increment_phi, -10 * DEGREES,
frame.increment_theta, -20 * DEGREES,
run_time=3
)
# Add ambient rotation
frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
# Play around with where the light is
light_text = Text("You can move around the light source")
light_text.move_to(surface_text)
light_text.fix_in_frame()
self.play(FadeTransform(surface_text, light_text))
light = self.camera.light_source
self.add(light)
light.save_state()
self.play(light.move_to, 3 * IN, run_time=5)
self.play(light.shift, 10 * OUT, run_time=5)
drag_text = Text("Try moving the mouse while pressing d or s")
drag_text.move_to(light_text)
drag_text.fix_in_frame()
self.play(FadeTransform(light_text, drag_text))
self.wait()
This scene shows an example of using a three-dimensional surface, and
the related usage has been briefly described in the notes.
- ``.fix_in_frame()`` makes the object not change with the view angle of the screen, and is always displayed at a fixed position on the screen.
OpeningManimExample
-------------------
.. manim-example:: OpeningManimExample
:media: ../_static/example_scenes/OpeningManimExample.mp4
class OpeningManimExample(Scene):
def construct(self):
title = TexText("This is some \\LaTeX")
basel = Tex(
"\\sum_{n=1}^\\infty "
"\\frac{1}{n^2} = \\frac{\\pi^2}{6}"
)
VGroup(title, basel).arrange(DOWN)
self.play(
Write(title),
FadeIn(basel, UP),
)
self.wait()
transform_title = Text("That was a transform")
transform_title.to_corner(UL)
self.play(
Transform(title, transform_title),
LaggedStartMap(FadeOut, basel, shift=DOWN),
)
self.wait()
fade_comment = Text(
"""
You probably don't want to overuse
Transforms, though, a simple fade often
looks nicer.
""",
font_size=36,
color=GREY_B,
)
fade_comment.next_to(
transform_title, DOWN,
buff=LARGE_BUFF,
aligned_edge=LEFT
)
self.play(FadeIn(fade_comment, shift=DOWN))
self.wait(3)
grid = NumberPlane((-10, 10), (-5, 5))
grid_title = Text(
"But manim is for illustrating math, not text",
)
grid_title.to_edge(UP)
grid_title.add_background_rectangle()
self.add(grid, grid_title) # Make sure title is on top of grid
self.play(
FadeOut(title, shift=LEFT),
FadeOut(fade_comment, shift=LEFT),
FadeIn(grid_title),
ShowCreation(grid, run_time=3, lag_ratio=0.1),
)
self.wait()
matrix = [[1, 1], [0, 1]]
linear_transform_title = VGroup(
Text("This is what the matrix"),
IntegerMatrix(matrix, include_background_rectangle=True),
Text("looks like")
)
linear_transform_title.arrange(RIGHT)
linear_transform_title.to_edge(UP)
self.play(
FadeOut(grid_title),
FadeIn(linear_transform_title),
)
self.play(grid.apply_matrix, matrix, run_time=3)
self.wait()
grid_transform_title = Text(
"And this is a nonlinear transformation"
)
grid_transform_title.set_stroke(BLACK, 5, background=True)
grid_transform_title.to_edge(UP)
grid.prepare_for_nonlinear_transform(100)
self.play(
ApplyPointwiseFunction(
lambda p: p + np.array([np.sin(p[1]), np.sin(p[0]), 0]),
grid,
run_time=5,
),
FadeOut(linear_transform_title),
FadeIn(grid_transform_title),
)
self.wait()
This scene is a comprehensive application of a two-dimensional scene.
After seeing these scenes, you have already understood part of the
usage of manim. For more examples, see `the video code of 3b1b <https://github.com/3b1b/videos>`_.

View file

@ -1,18 +0,0 @@
Getting Started
===============
Todd Zimmerman put together `a very nice tutorial`_ on getting started with
``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/2019/01/08/getting-started-animating-with-manim-and-python-3-7/
.. toctree::
:caption: Contents
:maxdepth: 2
learning_by_example
mathematical_objects
animating_mobjects
making_a_scene

View file

@ -0,0 +1,67 @@
Installation
============
Manim runs on Python 3.8.
System requirements are
- `FFmpeg <https://ffmpeg.org/>`__
- `OpenGL <https://www.opengl.org//>`__ (included in python package ``PyOpenGL``)
- `LaTeX <https://www.latex-project.org>`__ (optional, if you want to use LaTeX)
- `cairo <https://www.cairographics.org/>`_ (included in python package ``pycairo``. optional, if you want to use ``Text`` in manim)
Directly
--------
If you want to hack on manimlib itself, clone this repository and in
that directory execute:
.. code-block:: sh
# Install python requirements
pip install -r requirements.txt
# Try it out
python -m manim example_scenes.py OpeningManimExample
If you run the above command and no error message appears,
then you have successfully installed all the environments required by manim.
Directly (Windows)
------------------
1. `Install
FFmpeg <https://www.wikihow.com/Install-FFmpeg-on-Windows>`__, and make sure that its path is in the PATH environment variable.
2. Install a LaTeX distribution.
`TeXLive-full <http://tug.org/texlive/>`__ is recommended.
3. Install the remaining Python packages.
.. code-block:: sh
git clone https://github.com/3b1b/manim.git
cd manim
pip install -r requirements.txt
python manim.py example_scenes.py OpeningManimExample
For Anaconda
------------
- Install FFmpeg and LaTeX as above.
- Create a conda environment using
.. code-block:: sh
git clone https://github.com/3b1b/manim.git
cd manim
conda env create -f environment.yml
Using virtualenv and virtualenvwrapper
--------------------------------------
After installing ``virtualenv`` and ``virtualenvwrapper``
.. code-block:: sh
git clone https://github.com/3b1b/manim.git
mkvirtualenv -a manim -r requirements.txt manim
python -m manim example_scenes.py OpeningManimExample

View file

@ -1,131 +0,0 @@
Learning by Example
===================
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()
square = Square()
square.flip(RIGHT)
square.rotate(-3 * TAU / 8)
circle.set_fill(PINK, opacity=0.5)
self.play(ShowCreation(square))
self.play(Transform(square, circle))
self.play(FadeOut(square))
.. raw:: html
<video width="560" height="315" controls>
<source src="../_static/SquareToCircle.mp4" type="video/mp4">
</video>
.. 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
:lineno-start: 7
square.flip(RIGHT)
square.rotate(-3 * TAU / 8)
circle.set_fill(PINK, opacity=0.5)
``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
:lineno-start: 11
self.play(ShowCreation(square))
self.play(Transform(square, circle))
self.play(FadeOut(square))
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`.

View file

@ -1,15 +0,0 @@
Making a Scene
==============
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

View file

@ -1,13 +0,0 @@
Mathematical Objects
====================
Everything that appears on screen in a manim video is a
:class:`~mobject.mobject.Mobject`, or Mathematical Object. A
:class:`~mobject.mobject.Mobject`'s appearance is determined by 3
factors:
* ``m.points``, an Nx3 ``numpy.array`` specifying how to draw ``m``
* ``m``'s style attributes, such as ``m.color``, ``m.stroke_width``, and
``m.fill_opacity``
* ``m.submobjects``, a list of :class:`~mobject.mobject.Mobject` instances that
are considered part of ``m``

View file

@ -0,0 +1,256 @@
Quick Start
===========
After installing the manim environment according to the instructions on the
:doc:`installation` page, you can try to make a scene yourself from scratch.
First, create a new ``.py`` file (such as ``start.py``) according to the following
directory structure:
.. code-block:: text
:emphasize-lines: 8
manim/
├── manimlib/
│ ├── animation/
│ ├── ...
│ └── window.py
├── custom_default.yml
├── manim.py
└── start.py
And paste the following code (I will explain the function of each line in detail later):
.. code-block:: python
:linenos:
from manimlib.imports import *
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
self.add(circle)
And run this command:
.. code-block:: sh
python manim.py start.py SquareToCircle
A window will pop up on the screen. And then you can :
- scroll the middle mouse button to move the screen up and down
- hold down the :kbd:`z` on the keyboard while scrolling the middle mouse button to zoom the screen
- hold down the :kbd:`s` key on the keyboard and move the mouse to pan the screen
- hold down the :kbd:`d` key on the keyboard and move the mouse to change the three-dimensional perspective.
Finally, you can close the window and exit the program by pressing :kbd:`q`.
Run this command again:
.. code-block:: sh
python manim.py start.py SquareToCircle -os
At this time, no window will pop up. When the program is finished, this rendered
image will be automatically opened (saved in the subdirectory ``images/`` of the same
level directory of ``start.py`` by default):
.. image:: ../_static/quickstart/SquareToCircle.png
:align: center
Make an image
-------------
Next, let's take a detailed look at what each row does.
**Line 1**:
.. code-block:: python
from manimlib.imports import *
This will import all the classes that may be used when using manim.
**Line 3**:
.. code-block:: python
class SquareToCircle(Scene):
Create a :class:`Scene` subclass ``SquareToCircle``, which will be
the scene you write and render.
**Line 4**:
.. code-block:: python
def construct(self):
Write the ``construct()`` method, the content of which will determine
how to create the mobjects in the screen and what operations need to be performed.
**Line 5**:
.. code-block:: python
circle = Circle()
Create a circle (an instance of the :class:`Circle` class), called ``circle``
**Line 6~7**:
.. code-block:: python
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
Set the circle style by calling the circle's method.
- The ``.set_fill()`` method sets the fill color of this circle to blue (``BLUE``, defined in :doc:`../documentation/constants`), and the fill transparency to 0.5.
- The ``.set_stroke()`` method sets the stroke color of this circle to dark blue (``BLUE_E``, defined in :doc:`../documentation/constants`), and the stroke width to 4.
**Line 9**:
.. code-block:: python
self.add(circle)
Add this circle to the screen through the ``.add()`` method of :class:`Scene`.
Add animations
--------------
Let's change some codes and add some animations to make videos instead of just pictures.
.. code-block:: python
:linenos:
from manimlib.imports import *
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square = Square()
self.play(ShowCreation(square))
self.wait()
self.play(ReplacementTransform(square, circle))
self.wait()
Run this command this time:
.. code-block:: sh
python manim.py start.py SquareToCircle
The pop-up window will play animations of drawing a square and transforming
it into a circle. If you want to save this video, run:
.. code-block:: sh
python manim.py start.py SquareToCircle -ow
This time there will be no pop-up window, but the video file (saved in the subdirectory
``videos/`` of the same level directory of ``start.py`` by default) will be automatically
opened after the operation is over:
.. raw:: html
<video class="manim-video" controls loop autoplay src="../_static/quickstart/SquareToCircle.mp4"></video>
Let's take a look at the code this time. The first 7 lines are the same as the previous
ones, and the 8th line is similar to the 5th line, which creates an instance of the
:class:`Square` class and named it ``square``.
**Line 10**:
.. code-block:: python
self.play(ShowCreation(square))
An animation is played through :class:`Scene`'s ``.play()`` method. :class:`ShowCreation`
is an animation that shows the process of creating a given mobject.
``self.play(ShowCreation(square))`` is to play the animation of creating ``square``.
**Line 11**:
.. code-block:: python
self.wait()
Use :class:`Scene`'s ``.wait()`` method to pause (default 1s), you can pass in
parameters to indicate the pause time (for example, ``self.wait(3)`` means pause for 3s).
**Line 12**:
.. code-block:: python
self.play(ReplacementTransform(square, circle))
Play the animation that transforms ``square`` into ``circle``.
``ReplacementTransform(A, B)`` means to transform A into B's pattern and replace A with B.
**Line 13**: Same as line 11, pause for 1s.
Enable interaction
------------------
Interaction is a new feature of the new version. You can add the following line
at the end of the code to enable interaction:
.. code-block:: python
self.embed()
Then run ``python manim.py start.py SquareToCircle``.
After the previous animation is executed, the ipython terminal will be opened on
the command line. After that, you can continue to write code in it, and the statement
you entered will be executed immediately after pressing :kbd:`Enter`.
For example: input the following lines (without comment lines) into it respectively
(``self.play`` can be abbreviated as ``play`` in this mode):
.. code-block:: python
# Stretched 4 times in the vertical direction
play(circle.stretch, 4, {"dim": 0})
# Rotate the ellipse 90°
play(Rotate(circle, TAU / 4))
# Move 2 units to the right and shrink to 1/4 of the original
play(circle.shift, 2 * RIGHT, circle.scale, 0.25)
# Insert 10 curves into circle for non-linear transformation (no animation will play)
circle.insert_n_curves(10)
# Apply a complex transformation of f(z)=z^2 to all points on the circle
play(circle.apply_complex_function, lambda z: z**2)
# Close the window and exit the program
exit()
You will get an animation similar to the following:
.. raw:: html
<video class="manim-video" controls loop autoplay src="../_static/quickstart/SquareToCircleEmbed.mp4"></video>
If you want to enter the interactive mode directly, you don't have to write an
empty scene containing only ``self.embed()``, you can directly run the following command
(this will enter the ipython terminal while the window pops up):
.. code-block:: sh
python manim.py
You succeeded!
--------------
After reading the above content, you already know how to use manim.
Below you can see some examples, in the :doc:`example_scenes` page.
But before that, you'd better have a look at the :doc:`configuration` of manim.

View file

@ -0,0 +1,129 @@
Manim's structure
=================
Manim's directory structure
---------------------------
The manim directory looks very complicated, with a lot of files,
but the structure is clear.
Below is the directory structure of manim:
.. code-block:: text
├── manim.py # Manim command entry
├── custom_default.yml # Default configuration
└── manimlib/ # manim library
├── __init__.py # run from here
├── config.py # Process CLI flags
├── constants.py # Defined some constants
├── extract_scene.py # Extract and run the scene
├── imports.py # Import all required files in manimlib
├── shader_wrapper.py # Shaders' Wrapper for convenient control
├── window.py # Playback window
├── tex_templates/ # Templates preset for LaTeX
│ ├── tex_templates.tex # Tex template (will be compiled with latex, default)
│ └── ctex_templates.tex # Tex template that support Chinese (will be compiled with xelatex)
├── camera/
│ └── camera.py # Including Camera and CameraFrame
├── scene/
│ ├── scene_file_writer.py # Used to write scene to video file
│ ├── scene.py # The basic Scene class
│ ├── three_d_scene.py # Three-dimensional scene
│ ├── graph_scene.py # GraphScene (with coordinate axis)
│ ├── reconfigurable_scene.py
│ ├── sample_space_scene.py # Probability related sample space scene
│ └── vector_space_scene.py # Vector field scene
├── animation/
│ ├── animation.py # The basic class of animation
│ ├── composition.py # Animation group
│ ├── creation.py # Animation related to Create
│ ├── fading.py # Fade related animation
│ ├── growing.py # Animation related to Grow
│ ├── indication.py # Some animations for emphasis
│ ├── movement.py # Animation related to movement
│ ├── numbers.py # Realize changes to DecimalNumber
│ ├── rotation.py # Animation related to rotation
│ ├── specialized.py # Some uncommon animations for special projects
│ ├── transform_matching_parts.py # Transform which can automatically match parts
│ ├── transform.py # Some Transforms
│ └── update.py # Realize update from function
├── mobject/
│ ├── mobject.py # The basic class of all math object
│ ├── types/ # 4 types of mobject
│ │ ├── dot_cloud.py # Dot cloud (an subclass of PMobject)
│ │ ├── image_mobject.py # Insert pictures
│ │ ├── point_cloud_mobject.py # PMobject (mobject composed of points)
│ │ ├── surface.py # ParametricSurface
│ │ └── vectorized_mobject.py # VMobject (vectorized mobject)
│ ├── svg/ # mobject related to svg
│ │ ├── svg_mobject.py # SVGMobject
│ │ ├── brace.py # Brace
│ │ ├── drawings.py # Some special mobject of svg image
│ │ ├── tex_mobject.py # Tex and TexText implemented by LaTeX
│ │ └── text_mobject.py # Text implemented by cairo
│ ├── changing.py # Dynamically changing mobject
│ ├── coordinate_systems.py # coordinate system
│ ├── frame.py # mobject related to frame
│ ├── functions.py # ParametricFunction
│ ├── geometry.py # geometry mobjects
│ ├── matrix.py # matrix
│ ├── mobject_update_utils.py # some defined updater
│ ├── number_line.py # Number line
│ ├── numbers.py # Numbers that can be changed
│ ├── probability.py # mobject related to probability
│ ├── shape_matchers.py # mobject adapted to the size of other objects
│ ├── three_dimensions.py # Three-dimensional objects
│ ├── value_tracker.py # ValueTracker which storage number
│ └── vector_field.py # VectorField
├── once_useful_constructs/ # 3b1b's Common scenes written for some videos
│ └── ...
├── shaders/ # GLSL scripts for rendering
│ ├── simple_vert.glsl # a simple glsl script for position
│ ├── insert/ # glsl scripts to be inserted in other glsl scripts
│ │ ├── NOTE.md # explain how to insert glsl scripts
│ │ └── ... # useful scripts
│ ├── image/ # glsl for images
│ │ └── ... # containing shaders for vertex and fragment
│ ├── quadratic_bezier_fill/ # glsl for the fill of quadratic bezier curve
│ │ └── ... # containing shaders for vertex, fragment and geometry
│ ├── quadratic_bezier_stroke/ # glsl for the stroke of quadratic bezier curve
│ │ └── ... # containing shaders for vertex, fragment and geometry
│ ├── surface/ # glsl for surfaces
│ │ └── ... # containing shaders for vertex and fragment
│ ├── textured_surface/ # glsl for textured_surface
│ │ └── ... # containing shaders for vertex and fragment
│ └── true_dot/ # glsl for a dot
│ └── ... # containing shaders for vertex, fragment and geometry
└── utils/ # Some useful utility functions
├── bezier.py # For bezier curve
├── color.py # For color
├── config_ops.py # Process CONFIG
├── customization.py # Read from custom_default.yml
├── debug.py # Utilities for debugging in program
├── family_ops.py # Process family members
├── file_ops.py # Process files and directories
├── images.py # Read image
├── iterables.py # Functions related to list/dictionary processing
├── paths.py # Curve path
├── rate_functions.py # Some defined rate_functions
├── simple_functions.py # Some commonly used functions
├── sounds.py # Process sounds
├── space_ops.py # Space coordinate calculation
├── strings.py # Process strings
└── tex_file_writing.py # Use LaTeX to write strings as svg
Inheritance structure of manim's classes
----------------------------------------
`Here <https://github.com/3b1b/manim/files/5824383/manim_shaders_structure.pdf>`_
is a pdf showed inheritance structure of manim's classes, large,
but basically all classes have included:
.. image:: ../_static/manim_shaders_structure.png
Manim execution process
-----------------------
.. image:: ../_static/manim_shaders_process_en.png

View file

@ -1,30 +1,35 @@
.. Manim documentation master file, created by
sphinx-quickstart on Mon May 27 14:19:19 2019.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Manim's documentation
=====================
Welcome to Manim's documentation!
=================================
.. image:: ../../logo/white_with_name.png
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.
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/>`_.
And here is a Chinese version of this documentation: https://manim.ml/shaders
.. toctree::
:maxdepth: 2
:caption: Contents
:maxdepth: 2
:caption: Getting Started
about
installation/index
getting_started/index
coordinate
animation
constants
getting_started/installation
getting_started/quickstart
getting_started/configuration
getting_started/example_scenes
getting_started/config
getting_started/structure
.. toctree::
:maxdepth: 2
:caption: Documentation
Indices and tables
==================
documentation/constants
documentation/custom_default
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. toctree::
:maxdepth: 2
:caption: Development
development/changelog
development/contributing
development/about

View file

@ -1,12 +0,0 @@
Installation
============
Instructions on installing Manim
.. toctree::
:maxdepth: 2
:caption: Contents
linux
mac
windows

View file

@ -1,64 +0,0 @@
Linux
=====
Ubuntu
------
Install system libraries::
# apt install sox ffmpeg libcairo2 libcairo2-dev
Install Latex distribution::
# apt install texlive-full
Install manim via pypi::
# pip3 install manimlib
OR Install manim via the git repository with venv::
$ git clone https://github.com/3b1b/manim
$ cd manim
$ python3 -m venv ./
$ source bin/activate
$ pip3 install -r requirement.txt
To use manim in virtual environment you need to activate the environment with
the ``activate`` binary by doing ``source bin/activate``, to exit use the ``deactivate`` command.
.. note:: The git repository is updated first before the one on pypi. The git repository also
includes project files used to produce 3b1b videos. Some of the old projects might not
work as due to api changes.
.. note:: The required latex packages are dictated by
``manimlib/tex_template.tex`` which ``texlive-full`` will satisfy. The download size
can be quite large. If you wish to install only the packages required to use
manim, substitude ``texlive-full`` with::
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/

View file

@ -1,12 +0,0 @@
Mac
===
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)

View file

@ -1,60 +0,0 @@
Windows
=======
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 ``pycairo1.18.0cp37cp37mwin32.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``

View file

@ -0,0 +1,108 @@
from docutils import nodes
from docutils.parsers.rst import directives, Directive
import jinja2
import os
class skip_manim_node(nodes.Admonition, nodes.Element):
pass
def visit(self, node, name=""):
self.visit_admonition(node, name)
def depart(self, node):
self.depart_admonition(node)
class ManimExampleDirective(Directive):
has_content = True
required_arguments = 1
optional_arguments = 0
option_spec = {
"hide_code": bool,
"media": str,
}
final_argument_whitespace = True
def run(self):
hide_code = "hide_code" in self.options
scene_name = self.arguments[0]
media_file_name = self.options["media"]
source_block = [
".. code-block:: python",
"",
*[" " + line for line in self.content],
]
source_block = "\n".join(source_block)
state_machine = self.state_machine
document = state_machine.document
if any(media_file_name.endswith(ext) for ext in [".png", ".jpg", ".gif"]):
is_video = False
else:
is_video = True
rendered_template = jinja2.Template(TEMPLATE).render(
scene_name=scene_name,
scene_name_lowercase=scene_name.lower(),
hide_code=hide_code,
is_video=is_video,
media_file_name=media_file_name,
source_block=source_block,
)
state_machine.insert_input(
rendered_template.split("\n"), source=document.attributes["source"]
)
return []
def setup(app):
app.add_node(skip_manim_node, html=(visit, depart))
setup.app = app
setup.config = app.config
setup.confdir = app.confdir
app.add_directive("manim-example", ManimExampleDirective)
metadata = {"parallel_read_safe": False, "parallel_write_safe": True}
return metadata
TEMPLATE = r"""
{% if not hide_code %}
.. raw:: html
<div class="manim-example">
{% endif %}
{% if is_video %}
.. raw:: html
<video id="{{ scene_name_lowercase }}" class="manim-video" controls loop autoplay src="{{ media_file_name }}"></video>
{% else %}
.. image:: {{ media_file_name }}
:align: center
:name: {{ scene_name_lowercase }}
{% endif %}
{% if not hide_code %}
.. raw:: html
<h5 class="example-header">{{ scene_name }}<a class="headerlink" href="#{{ scene_name_lowercase }}"></a></h5>
{{ source_block }}
{% endif %}
.. raw:: html
</div>
"""

View file

@ -1,20 +1,29 @@
name: manim
name: manim_shaders
channels:
- defaults
- conda-forge
dependencies:
- python=3.7
- cairo
- ffmpeg
- colour==0.1.5
- numpy==1.15.0
- pillow==5.2.0
- scipy==1.1.0
- tqdm==4.24.0
- opencv==3.4.2
- pycairo==1.18.0
- pydub==0.23.0
- ffmpeg
- pygments==2.6.1
- pip
- pip:
- pyreadline
- pyreadline
- matplotlib
- mapbox-earcut
- moderngl_window
- screeninfo
- argparse
- colour
- numpy
- Pillow
- progressbar
- scipy
- sympy
- tqdm
- moderngl
- pydub
- pyyaml
- validators
- ipython
- PyOpenGL
- pycairo

View file

@ -1,73 +1,98 @@
#!/usr/bin/env python
from manimlib.imports import *
# To watch one of these scenes, run the following:
# python -m manim example_scenes.py SquareToCircle -pl
#
# Use the flag -l for a faster rendering at a lower
# quality.
# python -m manim example_scenes.py SquareToCircle
# Use -s to skip to the end and just save the final frame
# Use the -p to have the animation (or image, if -s was
# used) pop up once done.
# Use -w to write the animation to a file
# Use -o to write it to a file and open it once done
# Use -n <number> to skip ahead to the n'th animation of a scene.
# Use -r <number> to specify a resolution (for example, -r 1080
# for a 1920x1080 video)
class OpeningManimExample(Scene):
def construct(self):
title = TextMobject("This is some \\LaTeX")
basel = TexMobject(
title = TexText("This is some \\LaTeX")
basel = Tex(
"\\sum_{n=1}^\\infty "
"\\frac{1}{n^2} = \\frac{\\pi^2}{6}"
)
VGroup(title, basel).arrange(DOWN)
self.play(
Write(title),
FadeInFrom(basel, UP),
FadeIn(basel, UP),
)
self.wait()
transform_title = TextMobject("That was a transform")
transform_title.to_corner(UP + LEFT)
transform_title = Text("That was a transform")
transform_title.to_corner(UL)
self.play(
Transform(title, transform_title),
LaggedStart(*map(FadeOutAndShiftDown, basel)),
LaggedStartMap(FadeOut, basel, shift=DOWN),
)
self.wait()
grid = NumberPlane()
grid_title = TextMobject("This is a grid")
grid_title.scale(1.5)
grid_title.move_to(transform_title)
fade_comment = Text(
"""
You probably don't want to overuse
Transforms, though, a simple fade often
looks nicer.
""",
font_size=36,
color=GREY_B,
)
fade_comment.next_to(
transform_title, DOWN,
buff=LARGE_BUFF,
aligned_edge=LEFT
)
self.play(FadeIn(fade_comment, shift=DOWN))
self.wait(3)
grid = NumberPlane((-10, 10), (-5, 5))
grid_title = Text(
"But manim is for illustrating math, not text",
)
grid_title.to_edge(UP)
grid_title.add_background_rectangle()
self.add(grid, grid_title) # Make sure title is on top of grid
self.play(
FadeOut(title),
FadeInFromDown(grid_title),
FadeOut(title, shift=LEFT),
FadeOut(fade_comment, shift=LEFT),
FadeIn(grid_title),
ShowCreation(grid, run_time=3, lag_ratio=0.1),
)
self.wait()
grid_transform_title = TextMobject(
"That was a non-linear function \\\\"
"applied to the grid"
matrix = [[1, 1], [0, 1]]
linear_transform_title = VGroup(
Text("This is what the matrix"),
IntegerMatrix(matrix, include_background_rectangle=True),
Text("looks like")
)
grid_transform_title.move_to(grid_title, UL)
grid.prepare_for_nonlinear_transform()
linear_transform_title.arrange(RIGHT)
linear_transform_title.to_edge(UP)
self.play(
grid.apply_function,
lambda p: p + np.array([
np.sin(p[1]),
np.sin(p[0]),
0,
]),
run_time=3,
FadeOut(grid_title),
FadeIn(linear_transform_title),
)
self.play(grid.apply_matrix, matrix, run_time=3)
self.wait()
grid_transform_title = Text(
"And this is a nonlinear transformation"
)
grid_transform_title.set_stroke(BLACK, 5, background=True)
grid_transform_title.to_edge(UP)
grid.prepare_for_nonlinear_transform(100)
self.play(
Transform(grid_title, grid_transform_title)
ApplyPointwiseFunction(
lambda p: p + np.array([np.sin(p[1]), np.sin(p[0]), 0]),
grid,
run_time=5,
),
FadeOut(linear_transform_title),
FadeIn(grid_transform_title),
)
self.wait()
@ -75,62 +100,404 @@ class OpeningManimExample(Scene):
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square = Square()
square.flip(RIGHT)
square.rotate(-3 * TAU / 8)
circle.set_fill(PINK, opacity=0.5)
self.play(ShowCreation(square))
self.play(Transform(square, circle))
self.play(FadeOut(square))
self.wait()
self.play(ReplacementTransform(square, circle))
self.wait()
# This opens an iPython termnial where you can keep writing
# lines as if they were part of this construct method
self.embed()
# Try typing the following lines
# self.play(circle.stretch, 4, {"dim": 0})
# self.play(Rotate(circle, TAU / 4))
# self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25)
# circle.insert_n_curves(10)
# self.play(circle.apply_complex_function, lambda z: z**2)
class WarpSquare(Scene):
class AnimatingMethods(Scene):
def construct(self):
square = Square()
self.play(ApplyPointwiseFunction(
lambda point: complex_to_R3(np.exp(R3_to_complex(point))),
square
))
grid = Tex(r"\pi").get_grid(10, 10, height=4)
self.add(grid)
# If you pass in a mobject method to the scene's "play" function,
# it will apply an animation interpolating between the mobject's
# initial state and whatever happens when you apply that method.
# For example, calling grid.shift(2 * LEFT) would shift it two units
# to the left, but the following line animates that motion.
self.play(grid.shift, 2 * LEFT)
# The same applies for any method, including those setting colors.
self.play(grid.set_submobject_colors_by_gradient, BLUE, GREEN)
self.play(grid.set_height, TAU - MED_SMALL_BUFF)
self.wait()
# The method Mobject.apply_complex_function lets you apply arbitrary
# complex functions, treating the points defining the mobject as
# complex numbers.
self.play(grid.apply_complex_function, np.exp, run_time=5)
self.wait()
# Even more generally, you could apply Mobject.apply_function,
# which takes in functions form R^3 to R^3
self.play(
grid.apply_function,
lambda p: [
p[0] + 0.5 * math.sin(p[1]),
p[1] + 0.5 * math.sin(p[0]),
p[2]
],
run_time=5,
)
self.wait()
class WriteStuff(Scene):
class TextExample(Scene):
def construct(self):
example_text = TextMobject(
"This is some text",
tex_to_color_map={"text": YELLOW}
# To run this scene properly, you should have "Consolas" font in your computer
# for full usage, you can see https://github.com/3b1b/manim/pull/680
text = Text("Here is a text", font="Consolas", font_size=90)
difference = Text(
"""
The most important difference between Text and TexText is that\n
you can change the font more easily, but can't use the LaTeX grammar
""",
font="Arial", font_size=24,
# t2c is a dict that you can choose color for different text
t2c={"Text": BLUE, "TexText": BLUE, "LaTeX": ORANGE}
)
example_tex = TexMobject(
"\\sum_{k=1}^\\infty {1 \\over k^2} = {\\pi^2 \\over 6}",
)
group = VGroup(example_text, example_tex)
group.arrange(DOWN)
group.set_width(FRAME_WIDTH - 2 * LARGE_BUFF)
VGroup(text, difference).arrange(DOWN, buff=1)
self.play(Write(text))
self.play(FadeIn(difference, UP))
self.wait(3)
self.play(Write(example_text))
self.play(Write(example_tex))
fonts = Text(
"And you can also set the font according to different words",
font="Arial",
t2f={"font": "Consolas", "words": "Consolas"},
t2c={"font": BLUE, "words": GREEN}
)
slant = Text(
"And the same as slant and weight",
font="Consolas",
t2s={"slant": ITALIC},
t2w={"weight": BOLD},
t2c={"slant": ORANGE, "weight": RED}
)
VGroup(fonts, slant).arrange(DOWN, buff=0.8)
self.play(FadeOut(text), FadeOut(difference, shift=DOWN))
self.play(Write(fonts))
self.wait()
self.play(Write(slant))
self.wait()
class TexTransformExample(Scene):
def construct(self):
to_isolate = ["B", "C", "=", "(", ")"]
lines = VGroup(
# Surrounding substrings with double braces
# will ensure that those parts are separated
# out in the Tex. For example, here the
# Tex will have 5 submobjects, corresponding
# to the strings [A^2, +, B^2, =, C^2]
Tex("{{A^2}} + {{B^2}} = {{C^2}}"),
Tex("{{A^2}} = {{C^2}} - {{B^2}}"),
# Alternatively, you can pass in the keyword argument
# "isolate" with a list of strings that should be out as
# their own submobject. So both lines below are equivalent
# to what you'd get by wrapping every instance of "B", "C"
# "=", "(" and ")" with double braces
Tex("{{A^2}} = (C + B)(C - B)", isolate=to_isolate),
Tex("A = \\sqrt{(C + B)(C - B)}", isolate=to_isolate)
)
lines.arrange(DOWN, buff=LARGE_BUFF)
for line in lines:
line.set_color_by_tex_to_color_map({
"A": BLUE,
"B": TEAL,
"C": GREEN,
})
play_kw = {"run_time": 2}
self.add(lines[0])
# The animation TransformMatchingTex will line up parts
# of the source and target which have matching tex strings.
# Here, giving it a little path_arc makes each part sort of
# rotate into their final positions, which feels appropriate
# for the idea of rearranging an equation
self.play(
TransformMatchingTex(
lines[0].copy(), lines[1],
path_arc=90 * DEGREES,
),
**play_kw
)
self.wait()
# Now, we could try this again on the next line...
self.play(
TransformMatchingTex(lines[1].copy(), lines[2]),
**play_kw
)
self.wait()
# ...and this looks nice enough, but since there's no tex
# in lines[2] which matches "C^2" or "B^2", those terms fade
# out to nothing while the C and B terms fade in from nothing.
# If, however, we want the C^2 to go to C, and B^2 to go to B,
# we can specify that with a key map.
self.play(FadeOut(lines[2]))
self.play(
TransformMatchingTex(
lines[1].copy(), lines[2],
key_map={
"C^2": "C",
"B^2": "B",
}
),
**play_kw
)
self.wait()
# And to finish off, a simple TransformMatchingShapes would work
# just fine. But perhaps we want that exponent on A^2 to transform into
# the square root symbol. At the moment, lines[2] treats the expression
# A^2 as a unit, so we might create a new version of the same line which
# separates out just the A. This way, when TransformMatchingTex lines up
# all matching parts, the only mismatch will be between the "^2" from
# new_line2 and the "\sqrt" from the final line. By passing in,
# transform_mismatches=True, it will transform this "^2" part into
# the "\sqrt" part.
new_line2 = Tex("{{A}}^2 = (C + B)(C - B)", isolate=to_isolate)
new_line2.replace(lines[2])
new_line2.match_style(lines[2])
self.play(
TransformMatchingTex(
new_line2, lines[3],
transform_mismatches=True,
),
**play_kw
)
self.wait(3)
self.play(FadeOut(lines, RIGHT))
# Alternatively, if you don't want to think about breaking up
# the tex strings deliberately, you can TransformMatchingShapes,
# which will try to line up all pieces of a source mobject with
# those of a target, regardless of the submobject hierarchy in
# each one, according to whether those pieces have the same
# shape (as best it can).
source = Text("the morse code", height=1)
target = Text("here come dots", height=1)
self.play(Write(source))
self.wait()
kw = {"run_time": 3, "path_arc": PI / 2}
self.play(TransformMatchingShapes(source, target, **kw))
self.wait()
self.play(TransformMatchingShapes(target, source, **kw))
self.wait()
class UpdatersExample(Scene):
def construct(self):
decimal = DecimalNumber(
0,
show_ellipsis=True,
num_decimal_places=3,
include_sign=True,
)
square = Square().to_edge(UP)
square = Square()
square.set_fill(BLUE_E, 1)
decimal.add_updater(lambda d: d.next_to(square, RIGHT))
decimal.add_updater(lambda d: d.set_value(square.get_center()[1]))
self.add(square, decimal)
# On all all frames, the constructor Brace(square, UP) will
# be called, and the mobject brace will set its data to match
# that of the newly constructed object
brace = always_redraw(Brace, square, UP)
text, number = label = VGroup(
Text("Width = "),
DecimalNumber(
0,
show_ellipsis=True,
num_decimal_places=2,
include_sign=True,
)
)
label.arrange(RIGHT)
# This ensures that the method deicmal.next_to(square)
# is called on every frame
always(label.next_to, brace, UP)
# You could also write the following equivalent line
# label.add_updater(lambda m: m.next_to(brace, UP))
# If the argument itself might change, you can use f_always,
# for which the arguments following the initial Mobject method
# should be functions returning arguments to that method.
# The following line ensures thst decimal.set_value(square.get_y())
# is called every frame
f_always(number.set_value, square.get_width)
# You could also write the following equivalent line
# number.add_updater(lambda m: m.set_value(square.get_width()))
self.add(square, brace, label)
# Notice that the brace and label track with the square
self.play(
square.to_edge, DOWN,
square.scale, 2,
rate_func=there_and_back,
run_time=5,
run_time=2,
)
self.wait()
self.play(
square.set_width, 5, {"stretch": True},
run_time=3,
)
self.wait()
self.play(
square.set_width, 2,
run_time=3
)
self.wait()
# See old_projects folder for many, many more
# In general, you can alway call Mobject.add_updater, and pass in
# a function that you want to be called on every frame. The function
# should take in either one argument, the mobject, or two arguments,
# the mobject and the amount of time since the last frame.
now = self.time
w0 = square.get_width()
square.add_updater(
lambda m: m.set_width(w0 * math.cos(self.time - now))
)
self.wait(4 * PI)
class SurfaceExample(Scene):
CONFIG = {
"camera_class": ThreeDCamera,
}
def construct(self):
surface_text = Text("For 3d scenes, try using surfaces")
surface_text.fix_in_frame()
surface_text.to_edge(UP)
self.add(surface_text)
self.wait(0.1)
torus1 = Torus(r1=1, r2=1)
torus2 = Torus(r1=3, r2=1)
sphere = Sphere(radius=3, resolution=torus1.resolution)
# You can texture a surface with up to two images, which will
# be interpreted as the side towards the light, and away from
# the light. These can be either urls, or paths to a local file
# in whatever you've set as the image directory in
# the custom_defaults.yml file
# day_texture = "EarthTextureMap"
# night_texture = "NightEarthTextureMap"
day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
surfaces = [
TexturedSurface(surface, day_texture, night_texture)
for surface in [sphere, torus1, torus2]
]
for mob in surfaces:
mob.shift(IN)
mob.mesh = SurfaceMesh(mob)
mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
# Set perspective
frame = self.camera.frame
frame.set_euler_angles(
theta=-30 * DEGREES,
phi=70 * DEGREES,
)
surface = surfaces[0]
self.play(
FadeIn(surface),
ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3),
)
for mob in surfaces:
mob.add(mob.mesh)
surface.save_state()
self.play(Rotate(surface, PI / 2), run_time=2)
for mob in surfaces[1:]:
mob.rotate(PI / 2)
self.play(
Transform(surface, surfaces[1]),
run_time=3
)
self.play(
Transform(surface, surfaces[2]),
# Move camera frame during the transition
frame.increment_phi, -10 * DEGREES,
frame.increment_theta, -20 * DEGREES,
run_time=3
)
# Add ambient rotation
frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
# Play around with where the light is
light_text = Text("You can move around the light source")
light_text.move_to(surface_text)
light_text.fix_in_frame()
self.play(FadeTransform(surface_text, light_text))
light = self.camera.light_source
self.add(light)
light.save_state()
self.play(light.move_to, 3 * IN, run_time=5)
self.play(light.shift, 10 * OUT, run_time=5)
drag_text = Text("Try moving the mouse while pressing d or s")
drag_text.move_to(light_text)
drag_text.fix_in_frame()
self.play(FadeTransform(light_text, drag_text))
self.wait()
class ControlsExample(Scene):
def setup(self):
self.textbox = Textbox()
self.checkbox = Checkbox()
self.color_picker = ColorSliders()
self.panel = ControlPanel(
Text("Text", size=0.5), self.textbox, Line(),
Text("Show/Hide Text", size=0.5), self.checkbox, Line(),
Text("Color of Text", size=0.5), self.color_picker
)
self.add(self.panel)
def construct(self):
text = Text("", size=2)
def text_updater(old_text):
assert(isinstance(old_text, Text))
new_text = Text(self.textbox.get_value(), size=old_text.size)
# new_text.align_data_and_family(old_text)
new_text.move_to(old_text)
if self.checkbox.get_value():
new_text.set_fill(
color=self.color_picker.get_picked_color(),
opacity=self.color_picker.get_picked_opacity()
)
else:
new_text.set_opacity(0)
old_text.become(new_text)
text.add_updater(text_updater)
self.add(MotionMobject(text))
self.textbox.set_value("Manim")
self.embed()
# See https://github.com/3b1b/videos for many, many more

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,107 +0,0 @@
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 = "diffyq/part1"
SCENES_IN_ORDER = [
WhenChangeIsEasier,
VectorFieldTest,
IntroducePendulum,
MultiplePendulumsOverlayed,
PeriodFormula,
FormulasAreLies,
MediumAnglePendulum,
MediumHighAnglePendulum,
HighAnglePendulum,
LowAnglePendulum,
SomeOfYouWatching,
SmallAngleApproximationTex,
VeryLowAnglePendulum,
FormulasAreLies,
TourOfDifferentialEquations,
WherePendulumLeads,
LongDoublePendulum,
# FollowThisThread,
StrogatzQuote,
ShowHorizontalDashedLine,
RabbitFoxPopulations,
RabbitFoxEquation,
# Something...
ShowSimpleTrajectory,
SimpleProjectileEquation,
SimpleProjectileEquationVGraphFreedom,
ShowGravityAcceleration,
UniversalGravityLawSymbols,
ExampleTypicalODE,
AnalyzePendulumForce,
ShowSineValues,
BuildUpEquation,
AirResistanceBrace,
ShowDerivativeVideo,
SubtleAirCurrents,
SimpleDampenedPendulum,
DefineODE,
SecondOrderEquationExample,
ODEvsPDEinFrames,
ProveTeacherWrong,
SetAsideSeekingSolution,
#
WriteInRadians,
XEqLThetaToCorner,
ComingUp,
InputLabel,
SoWhatIsThetaThen,
ReallyHardToSolve,
ReasonForSolution,
PhysicistPhaseSpace,
GleickQuote,
SpectrumOfStartingStates,
WritePhaseFlow,
AskAboutStability,
LoveExample,
PassageOfTime,
LovePhaseSpace,
ComparePhysicsToLove,
FramesComparingPhysicsToLove,
SetupToTakingManyTinySteps,
ShowClutterPrevention,
# VisualizeHeightSlopeCurvature,
VisualizeStates,
ReferencePiCollisionStateSpaces,
IntroduceVectorField,
XComponentArrows,
BreakingSecondOrderIntoTwoFirstOrder,
ShowPendulumPhaseFlow,
ShowHighVelocityCase,
TweakMuInFormula,
TweakMuInVectorField,
FromODEToVectorField,
LorenzVectorField,
ThreeBodiesInSpace,
AltThreeBodiesInSpace,
TwoBodiesInSpace,
TwoBodiesWithZPart,
ThreeBodyTitle,
ThreeBodySymbols,
#
HighAmplitudePendulum,
WritePhaseSpace,
#
AskAboutActuallySolving,
WriteODESolvingCode,
TakeManyTinySteps,
ManyStepsFromDifferentStartingPoints,
InaccurateComputation,
HungerForExactness,
ShowRect,
ShowSquare,
JumpToThisPoint,
ThreeBodyEquation,
ItGetsWorse,
ChaosTitle,
RevisitQuote,
EndScreen,
Thumbnail,
]

View file

@ -1,41 +0,0 @@
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 = "diffyq/part2"
SCENES_IN_ORDER = [
PartTwoOfTour,
HeatEquationIntroTitle,
BrownianMotion,
BlackScholes,
ContrastChapters1And2,
FourierSeriesIntro,
FourierSeriesIntroBackground20,
ExplainCircleAnimations,
# FourierSeriesIntroBackground4,
# FourierSeriesIntroBackground8,
# FourierSeriesIntroBackground12,
TwoDBodyWithManyTemperatures,
TwoDBodyWithManyTemperaturesGraph,
TwoDBodyWithManyTemperaturesContour,
BringTwoRodsTogether,
ShowEvolvingTempGraphWithArrows,
# TodaysTargetWrapper,
WriteHeatEquation,
ReactionsToInitialHeatEquation,
TalkThrough1DHeatGraph,
ShowCubeFormation,
CompareInputsOfGeneralCaseTo1D,
ContrastXChangesToTChanges,
ShowPartialDerivativeSymbols,
WriteHeatEquation,
ShowCurvatureToRateOfChangeIntuition,
ContrastPDEToODE,
TransitionToTempVsTime,
Show1DAnd3DEquations,
#
AskAboutWhereEquationComesFrom,
DiscreteSetup,
]

View file

@ -1,70 +0,0 @@
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,
]

View file

@ -1,65 +0,0 @@
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,
]

View file

@ -1,5 +0,0 @@
from active_projects.diffyq.part5.staging import *
OUTPUT_DIRECTORY = "diffyq/part5"
SCENES_IN_ORDER = [
]

View file

@ -1,23 +0,0 @@
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,
]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,515 +0,0 @@
from manimlib.imports import *
from active_projects.diffyq.part1.shared_constructs import *
class SomeOfYouWatching(TeacherStudentsScene):
CONFIG = {
"camera_config": {
"background_color": DARKER_GREY,
}
}
def construct(self):
screen = self.screen
screen.scale(1.25, about_edge=UL)
screen.set_fill(BLACK, 1)
self.add(screen)
self.teacher.change("raise_right_hand")
for student in self.students:
student.change("pondering", screen)
self.student_says(
"Well...yeah",
target_mode="tease"
)
self.wait(3)
class FormulasAreLies(PiCreatureScene):
def construct(self):
you = self.pi_creature
t2c = {
"{L}": BLUE,
"{g}": YELLOW,
"\\theta_0": WHITE,
"\\sqrt{\\,": WHITE,
}
kwargs = {"tex_to_color_map": t2c}
period_eq = TexMobject(
"\\text{Period} = 2\\pi \\sqrt{\\,{L} / {g}}",
**kwargs
)
theta_eq = TexMobject(
"\\theta(t) = \\theta_0 \\cos\\left("
"\\sqrt{\\,{L} / {g}} \\cdot t"
"\\right)",
**kwargs
)
equations = VGroup(theta_eq, period_eq)
equations.arrange(DOWN, buff=LARGE_BUFF)
for eq in period_eq, theta_eq:
i = eq.index_of_part_by_tex("\\sqrt")
eq.sqrt_part = eq[i:i + 4]
theta0 = theta_eq.get_part_by_tex("\\theta_0")
theta0_words = TextMobject("Starting angle")
theta0_words.next_to(theta0, UL)
theta0_words.shift(UP + 0.5 * RIGHT)
arrow = Arrow(
theta0_words.get_bottom(),
theta0,
color=WHITE,
tip_length=0.25,
)
bubble = SpeechBubble()
bubble.pin_to(you)
bubble.write("Lies!")
bubble.content.scale(2)
bubble.resize_to_content()
self.add(period_eq)
you.change("pondering", period_eq)
self.wait()
theta_eq.remove(*theta_eq.sqrt_part)
self.play(
TransformFromCopy(
period_eq.sqrt_part,
theta_eq.sqrt_part,
),
FadeIn(theta_eq)
)
theta_eq.add(*theta_eq.sqrt_part)
self.play(
FadeInFrom(theta0_words, LEFT),
GrowArrow(arrow),
)
self.wait()
self.play(you.change, "confused")
self.wait()
self.play(
you.change, "angry",
ShowCreation(bubble),
FadeInFromPoint(bubble.content, you.mouth),
equations.to_edge, LEFT,
FadeOut(arrow),
FadeOut(theta0_words),
)
self.wait()
def create_pi_creature(self):
return You().flip().to_corner(DR)
# class TourOfDifferentialEquations(Scene):
# def construct(self):
# pass
class SoWhatIsThetaThen(TeacherStudentsScene):
def construct(self):
ode = get_ode()
ode.to_corner(UL)
self.add(ode)
self.student_says(
"Okay, but then\\\\"
"what \\emph{is} $\\theta(t)$?"
)
self.wait()
self.play(self.teacher.change, "happy")
self.wait(2)
self.teacher_says(
"First, you must appreciate\\\\"
"a deep truth...",
added_anims=[self.get_student_changes(
*3 * ["confused"]
)]
)
self.wait(4)
class ProveTeacherWrong(TeacherStudentsScene):
def construct(self):
tex_config = {
"tex_to_color_map": {
"{\\theta}": BLUE,
"{\\dot\\theta}": YELLOW,
"{\\ddot\\theta}": RED,
}
}
func = TexMobject(
"{\\theta}(t)", "=",
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
**tex_config,
)
d_func = TexMobject(
"{\\dot\\theta}(t)", "=",
"-\\left(\\sqrt{g / L}\\right)",
"\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)",
**tex_config,
)
dd_func = TexMobject(
"{\\ddot\\theta}(t)", "=",
"-\\left(g / L\\right)",
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
**tex_config,
)
# ode = TexMobject(
# "\\ddot {\\theta}({t})", "=",
# "-\\mu \\dot {\\theta}({t})",
# "-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
# **tex_config,
# )
ode = get_ode()
arrows = [TexMobject("\\Downarrow") for x in range(2)]
VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7)
teacher = self.teacher
you = self.students[2]
self.student_thinks(ode)
you.add_updater(lambda m: m.look_at(func))
self.teacher_holds_up(func)
self.wait()
group = VGroup(arrows[0], d_func, arrows[1], dd_func)
group.arrange(DOWN)
group.move_to(func, DOWN)
arrow = Arrow(
group.get_corner(UL),
ode.get_top(),
path_arc=PI / 2,
)
q_marks = VGroup(*[
TexMobject("?").scale(1.5).next_to(
arrow.point_from_proportion(a),
UP
)
for a in np.linspace(0.2, 0.8, 5)
])
cycle_animation(VFadeInThenOut(
q_marks,
lag_ratio=0.2,
run_time=4,
rate_func=squish_rate_func(smooth, 0, 0.5)
))
self.play(
func.next_to, group, UP,
LaggedStartMap(
FadeInFrom, group,
lambda m: (m, UP)
),
teacher.change, "guilty",
you.change, "sassy",
)
rect = SurroundingRectangle(
VGroup(group, func)
)
dashed_rect = DashedVMobject(rect, num_dashes=75)
animated_rect = AnimatedBoundary(dashed_rect, cycle_rate=1)
self.wait()
self.add(animated_rect, q_marks)
self.play(
ShowCreation(arrow),
# FadeInFromDown(q_mark),
self.get_student_changes("confused", "confused")
)
self.wait(4)
self.change_student_modes(
*3 * ["pondering"],
self.teacher.change, "maybe"
)
self.wait(8)
class PhysicistPhaseSpace(PiCreatureScene):
def construct(self):
physy = self.pi_creature
name = TextMobject("Physicist")
name.scale(1.5)
name.to_corner(DL, buff=MED_SMALL_BUFF)
physy.next_to(name, UP, SMALL_BUFF)
VGroup(name, physy).shift_onto_screen()
axes = Axes(
x_min=-1,
x_max=10,
y_min=-1,
y_max=7,
)
axes.set_height(6)
axes.next_to(physy, RIGHT)
axes.to_edge(UP)
axes.set_stroke(width=1)
x_label = TextMobject("Position")
x_label.next_to(axes.x_axis.get_right(), UP)
y_label = TextMobject("Momentum")
y_label.next_to(axes.y_axis.get_top(), RIGHT)
title = TextMobject("Phase space")
title.scale(1.5)
title.set_color(YELLOW)
title.move_to(axes)
self.add(name, physy)
self.play(
physy.change, "angry",
Write(axes),
FadeInFromDown(title)
)
self.wait(2)
self.play(
GrowFromPoint(x_label, physy.get_corner(UR)),
physy.change, "raise_right_hand",
axes.x_axis.get_right()
)
self.play(
GrowFromPoint(y_label, physy.get_corner(UR)),
physy.look_at, axes.y_axis.get_top(),
)
self.wait(3)
def create_pi_creature(self):
return PiCreature(color=GREY).to_corner(DL)
class AskAboutActuallySolving(TeacherStudentsScene):
def construct(self):
ode = get_ode()
ode.to_corner(UL)
self.add(ode)
morty = self.teacher
self.student_says(
"Yeah yeah, but how do\\\\"
"you actually \\emph{solve} it?",
student_index=1,
target_mode="sassy",
added_anims=[morty.change, "thinking"],
)
self.change_student_modes(
"confused", "sassy", "confused",
look_at_arg=ode,
)
self.wait()
self.teacher_says(
"What do you mean\\\\ by ``solve''?",
target_mode="speaking",
added_anims=[self.get_student_changes(
*3 * ["erm"]
)]
)
self.play(self.students[1].change, "angry")
self.wait(3)
class HungerForExactness(TeacherStudentsScene):
def construct(self):
students = self.students
you = students[2]
teacher = self.teacher
ode = get_ode()
ode.to_corner(UL)
left_part = ode[:5]
friction_part = ode[5:11]
self.add(ode)
proposed_solution = TexMobject(
"\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}"
)
proposed_solution.next_to(
you.get_corner(UL), UP, buff=0.7
)
proposed_solution_rect = SurroundingRectangle(
proposed_solution, buff=MED_SMALL_BUFF,
)
proposed_solution_rect.set_color(BLUE)
proposed_solution_rect.round_corners()
solution_p1 = TexMobject(
"""
\\theta(t) = 2\\text{am}\\left(
\\frac{\\sqrt{2g + Lc_1} (t + c_2)}{2\\sqrt{L}},
\\frac{4g}{2g + Lc_1}
\\right)
""",
)
solution_p1.to_corner(UL)
solution_p2 = TexMobject(
"c_1, c_2 = \\text{Constants depending on initial conditions}"
)
solution_p2.set_color(LIGHT_GREY)
solution_p2.scale(0.75)
solution_p3 = TexMobject(
"""
\\text{am}(u, k) =
\\int_0^u \\text{dn}(v, k)\\,dv
"""
)
solution_p3.name = TextMobject(
"(Jacobi amplitude function)"
)
solution_p4 = TexMobject(
"""
\\text{dn}(u, k) =
\\sqrt{1 - k^2 \\sin^2(\\phi)}
"""
)
solution_p4.name = TextMobject(
"(Jacobi elliptic function)"
)
solution_p5 = TextMobject("Where $\\phi$ satisfies")
solution_p6 = TexMobject(
"""
u = \\int_0^\\phi \\frac{dt}{\\sqrt{1 - k^2 \\sin^2(t)}}
"""
)
solution = VGroup(
solution_p1,
solution_p2,
solution_p3,
solution_p4,
solution_p5,
solution_p6,
)
solution.arrange(DOWN)
solution.scale(0.7)
solution.to_corner(UL, buff=MED_SMALL_BUFF)
solution.set_stroke(width=0, background=True)
solution.remove(solution_p2)
solution_p1.add(solution_p2)
solution.remove(solution_p5)
solution_p6.add(solution_p5)
for part in [solution_p3, solution_p4]:
part.name.scale(0.7 * 0.7)
part.name.set_color(LIGHT_GREY)
part.name.next_to(part, RIGHT)
part.add(part.name)
self.student_says(
"Right, but like,\\\\"
"what \\emph{is} $\\theta(t)$?",
target_mode="sassy",
added_anims=[teacher.change, "guilty"],
)
self.wait()
self.play(
FadeInFromDown(proposed_solution),
RemovePiCreatureBubble(
you,
target_mode="raise_left_hand",
look_at_arg=proposed_solution,
),
teacher.change, "pondering",
students[0].change, "pondering",
students[1].change, "hesitant",
)
self.play(ShowCreation(proposed_solution_rect))
self.play(
proposed_solution.shift, 3 * RIGHT,
proposed_solution_rect.shift, 3 * RIGHT,
you.change, "raise_right_hand", teacher.eyes,
)
self.wait(3)
self.play(
FadeOut(proposed_solution),
FadeOut(proposed_solution_rect),
ode.move_to, self.hold_up_spot, DOWN,
ode.shift, LEFT,
teacher.change, "raise_right_hand",
self.get_student_changes(*3 * ["pondering"])
)
self.wait()
ode.save_state()
self.play(
left_part.move_to, friction_part, RIGHT,
left_part.match_y, left_part,
friction_part.to_corner, DR,
friction_part.fade, 0.5,
)
self.wait()
modes = ["erm", "sad", "sad", "horrified"]
for part, mode in zip(solution, modes):
self.play(
FadeInFrom(part, UP),
self.get_student_changes(
*3 * [mode],
look_at_arg=part,
)
)
self.wait()
self.wait(3)
self.change_student_modes("tired", "sad", "concerned_musician")
self.wait(4)
self.look_at(solution)
self.wait(5)
self.play(
FadeOutAndShift(solution, 2 * LEFT),
Restore(ode),
self.get_student_changes(
"sick", "angry", "tired",
)
)
self.wait(3)
mystery = TexMobject(
"\\theta(t) = ???",
tex_to_color_map={"\\theta": BLUE},
)
mystery.scale(2)
mystery.to_edge(UP)
mystery.set_stroke(width=0, background=True)
mystery_boundary = AnimatedBoundary(
mystery, stroke_width=1
)
self.play(
FadeInFromDown(mystery),
self.teacher.change, "pondering"
)
self.add(mystery_boundary, mystery)
self.change_all_student_modes("sad")
self.look_at(mystery)
self.wait(5)
# Define
self.student_says(
"Let $\\text{P}(\\mu, g, L; t)$ be a\\\\"
"function satisfying this ODE.",
student_index=0,
target_mode="speaking",
added_anims=[
FadeOut(mystery),
FadeOut(mystery_boundary),
ode.to_corner, UR
]
)
self.change_student_modes(
"hooray", "sassy", "sassy",
look_at_arg=students[0].eyes.get_corner(UR),
)
self.wait(2)
class ItGetsWorse(TeacherStudentsScene):
def construct(self):
self.teacher_says("It gets\\\\worse")
self.change_student_modes(
"hesitant", "pleading", "erm"
)
self.wait(5)

View file

@ -1,118 +0,0 @@
from manimlib.imports import *
Lg_formula_config = {
"tex_to_color_map": {
"\\theta_0": WHITE,
"{L}": BLUE,
"{g}": YELLOW,
},
}
class You(PiCreature):
CONFIG = {
"color": BLUE_C,
}
def get_ode():
tex_config = {
"tex_to_color_map": {
"{\\theta}": BLUE,
"{\\dot\\theta}": RED,
"{\\ddot\\theta}": YELLOW,
"{t}": WHITE,
"{\\mu}": WHITE,
}
}
ode = TexMobject(
"{\\ddot\\theta}({t})", "=",
"-{\\mu} {\\dot\\theta}({t})",
"-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
**tex_config,
)
return ode
def get_period_formula():
return TexMobject(
"2\\pi", "\\sqrt{\\,", "L", "/", "g", "}",
tex_to_color_map={
"L": BLUE,
"g": YELLOW,
}
)
def pendulum_vector_field_func(point, mu=0.1, g=9.8, L=3):
theta, omega = point[:2]
return np.array([
omega,
-np.sqrt(g / L) * np.sin(theta) - mu * omega,
0,
])
def get_vector_symbol(*texs, **kwargs):
config = {
"include_background_rectangle": True,
"bracket_h_buff": SMALL_BUFF,
"bracket_v_buff": SMALL_BUFF,
"element_alignment_corner": ORIGIN,
}
config.update(kwargs)
array = [[tex] for tex in texs]
return Matrix(array, **config)
def get_heart_var(index):
heart = SuitSymbol("hearts")
if index == 1:
heart.set_color(BLUE_C)
elif index == 2:
heart.set_color(GREEN)
heart.set_height(0.7)
index = Integer(index)
index.move_to(heart.get_corner(DR))
heart.add(index)
return heart
def get_heart_var_deriv(index):
heart = get_heart_var(index)
filler_tex = "T"
deriv = TexMobject("{d", filler_tex, "\\over", "dt}")
deriv.scale(2)
filler = deriv.get_part_by_tex(filler_tex)
heart.match_height(filler)
heart.move_to(filler)
heart.scale(1.5, about_edge=UL)
deriv.remove(filler)
deriv.add(heart)
deriv.heart = heart
return deriv
def get_love_equation1():
equation = VGroup(
get_heart_var_deriv(1),
TexMobject("=").scale(2),
TexMobject("a").scale(2),
get_heart_var(2)
)
equation.arrange(RIGHT)
equation[-1].shift(SMALL_BUFF * DL)
return equation
def get_love_equation2():
equation = VGroup(
get_heart_var_deriv(2),
TexMobject("=").scale(2),
TexMobject("-b").scale(2),
get_heart_var(1),
)
equation.arrange(RIGHT)
equation[-1].shift(SMALL_BUFF * DL)
return equation

File diff suppressed because it is too large Load diff

View file

@ -1,855 +0,0 @@
from manimlib.imports import *
from active_projects.diffyq.part1.shared_constructs import *
class SmallAngleApproximationTex(Scene):
def construct(self):
approx = TexMobject(
"\\sin", "(", "\\theta", ") \\approx \\theta",
tex_to_color_map={"\\theta": RED},
arg_separator="",
)
implies = TexMobject("\\Downarrow")
period = TexMobject(
"\\text{Period}", "\\approx",
"2\\pi \\sqrt{\\,{L} / {g}}",
**Lg_formula_config,
)
group = VGroup(approx, implies, period)
group.arrange(DOWN)
approx_brace = Brace(approx, UP, buff=SMALL_BUFF)
approx_words = TextMobject(
"For small $\\theta$",
tex_to_color_map={"$\\theta$": RED},
)
approx_words.scale(0.75)
approx_words.next_to(approx_brace, UP, SMALL_BUFF)
self.add(approx, approx_brace, approx_words)
self.play(
Write(implies),
FadeInFrom(period, LEFT)
)
self.wait()
class StrogatzQuote(Scene):
def construct(self):
quote = self.get_quote()
movers = VGroup(*quote[:-1].family_members_with_points())
for mover in movers:
mover.save_state()
disc = Circle(radius=0.05)
disc.set_stroke(width=0)
disc.set_fill(BLACK, 0)
disc.move_to(mover)
mover.become(disc)
self.play(
FadeInFrom(quote.author_part, LEFT),
LaggedStartMap(
# FadeInFromLarge,
# quote[:-1].family_members_with_points(),
Restore, movers,
lag_ratio=0.005,
run_time=2,
)
# FadeInFromDown(quote[:-1]),
# lag_ratio=0.01,
)
self.wait()
self.play(
Write(quote.law_part.copy().set_color(YELLOW)),
run_time=1,
)
self.wait()
self.play(
Write(quote.language_part.copy().set_color(BLUE)),
run_time=1.5,
)
self.wait(2)
def get_quote(self):
law_words = "laws of physics"
language_words = "language of differential equations"
author = "-Steven Strogatz"
quote = TextMobject(
"""
\\Large
``Since Newton, mankind has come to realize
that the laws of physics are always expressed
in the language of differential equations.''\\\\
""" + author,
alignment="",
arg_separator=" ",
substrings_to_isolate=[law_words, language_words, author]
)
quote.law_part = quote.get_part_by_tex(law_words)
quote.language_part = quote.get_part_by_tex(language_words)
quote.author_part = quote.get_part_by_tex(author)
quote.set_width(12)
quote.to_edge(UP)
quote[-2].shift(SMALL_BUFF * LEFT)
quote.author_part.shift(RIGHT + 0.5 * DOWN)
quote.author_part.scale(1.2, about_edge=UL)
return quote
class WriteInRadians(Scene):
def construct(self):
words = TextMobject("In radians")
words.set_color(YELLOW)
square = SurroundingRectangle(TexMobject("\\theta"))
square.next_to(words, UP)
self.play(ShowCreation(square))
self.play(Write(words), FadeOut(square))
self.wait()
class XEqLThetaToCorner(Scene):
def construct(self):
equation = TexMobject(
"x = L\\theta",
tex_to_color_map={
"x": GREEN,
"\\theta": BLUE,
}
)
equation.move_to(DOWN + 3 * RIGHT)
self.add(equation)
self.play(equation.to_corner, DL, {"buff": LARGE_BUFF})
self.wait()
class ComingUp(Scene):
CONFIG = {
"camera_config": {"background_color": DARKER_GREY}
}
def construct(self):
frame = ScreenRectangle(
stroke_width=0,
fill_color=BLACK,
fill_opacity=1,
height=6
)
title = TextMobject("Coming up")
title.scale(1.5)
title.to_edge(UP)
frame.next_to(title, DOWN)
animated_frame = AnimatedBoundary(frame)
self.add(frame, title, animated_frame)
self.wait(10)
class InputLabel(Scene):
def construct(self):
label = TextMobject("Input")
label.scale(1.25)
arrow = Vector(UP)
arrow.next_to(label, UP)
self.play(
FadeInFrom(label, UP),
GrowArrow(arrow)
)
self.wait()
class ReallyHardToSolve(Scene):
def construct(self):
words = TextMobject(
"They're", "really\\\\",
"freaking", "hard\\\\",
"to", "solve\\\\",
)
words.set_height(6)
self.wait()
for word in words:
wait_time = 0.05 * len(word)
self.add(word)
self.wait(wait_time)
self.wait()
class ReasonForSolution(Scene):
def construct(self):
# Words
eq_word = TextMobject("Differential\\\\Equation")
s_word = TextMobject("Solution")
u_word = TextMobject("Understanding")
c_word = TextMobject("Computation")
cu_group = VGroup(u_word, c_word)
cu_group.arrange(DOWN, buff=2)
group = VGroup(eq_word, s_word, cu_group)
group.arrange(RIGHT, buff=2)
# words = VGroup(eq_word, s_word, u_word, c_word)
# Arrows
arrows = VGroup(
Arrow(eq_word.get_right(), s_word.get_left()),
Arrow(s_word.get_right(), u_word.get_left()),
Arrow(s_word.get_right(), c_word.get_left()),
)
arrows.set_color(LIGHT_GREY)
new_arrows = VGroup(
Arrow(
eq_word.get_corner(UR),
u_word.get_left(),
path_arc=-60 * DEGREES,
),
Arrow(
eq_word.get_corner(DR),
c_word.get_left(),
path_arc=60 * DEGREES,
),
)
new_arrows.set_color(BLUE)
# Define first examples
t2c = {
"{x}": BLUE,
"{\\dot x}": RED,
}
equation = TexMobject(
"{\\dot x}(t) = k {x}(t)",
tex_to_color_map=t2c,
)
equation.next_to(eq_word, DOWN)
solution = TexMobject(
"{x}(t) = x_0 e^{kt}",
tex_to_color_map=t2c,
)
solution.next_to(s_word, DOWN, MED_LARGE_BUFF)
equation.align_to(solution, DOWN)
axes = Axes(
x_min=-1,
x_max=5.5,
y_min=-1,
y_max=4.5,
y_axis_config={"unit_size": 0.5}
)
axes.set_stroke(width=2)
graph_line = axes.get_graph(
lambda x: np.exp(0.4 * x)
)
graph_line.set_stroke(width=2)
graph = VGroup(axes, graph_line)
graph.scale(0.5)
graph.next_to(u_word, UP)
computation = TexMobject(
# "\\displaystyle "
"e^x = \\sum_{n=0}^\\infty "
"\\frac{x^n}{n!}"
)
computation.next_to(c_word, DOWN)
first_examples = VGroup(
equation, solution, graph, computation
)
# Second example
ode = get_ode()
ode.scale(0.75)
second_examples = VGroup(
ode,
TexMobject("???").set_color(LIGHT_GREY),
ScreenRectangle(
height=2,
stroke_width=1,
),
)
for fe, se in zip(first_examples, second_examples):
se.move_to(fe, DOWN)
ode.shift(2 * SMALL_BUFF * DOWN)
ode.add_to_back(BackgroundRectangle(ode[-4:]))
self.add(eq_word)
self.add(equation)
self.play(
FadeInFrom(s_word, LEFT),
GrowArrow(arrows[0]),
TransformFromCopy(equation, solution)
)
self.wait()
self.play(
FadeInFrom(c_word, UL),
GrowArrow(arrows[2]),
FadeInFrom(computation, UP)
)
self.wait()
self.play(
FadeInFrom(u_word, DL),
GrowArrow(arrows[1]),
FadeInFromDown(graph)
)
self.wait(2)
self.play(
FadeOut(first_examples),
FadeIn(second_examples[:2])
)
self.wait()
self.play(
arrows.fade, 0.75,
s_word.fade, 0.75,
second_examples[1].fade, 0.75,
ShowCreation(new_arrows[0]),
FadeIn(second_examples[2])
)
self.play(
ShowCreation(new_arrows[1]),
Animation(second_examples),
)
self.wait()
class WritePhaseSpace(Scene):
def construct(self):
word = TextMobject("Phase space")
word.scale(2)
word.shift(FRAME_WIDTH * LEFT / 4)
word.to_edge(UP)
word.add_background_rectangle()
lines = VGroup(*[
Line(v, 1.3 * v)
for v in compass_directions(50)
])
lines.replace(word, stretch=True)
lines.scale(1.5)
lines.set_stroke(YELLOW)
lines.shuffle()
self.add(word)
self.play(
ShowPassingFlashWithThinningStrokeWidth(
lines,
lag_ratio=0.002,
run_time=1.5,
time_width=0.9,
n_segments=5,
)
)
self.wait()
class GleickQuote(Scene):
def construct(self):
quote = TextMobject(
"``[Phase space is] one of the most\\\\",
"powerful inventions", "of modern science.''\\\\",
)
quote.power_part = quote.get_part_by_tex("power")
book = ImageMobject("ChaosBookCover")
book.set_height(5)
book.next_to(ORIGIN, LEFT)
book.to_edge(DOWN)
gleick = ImageMobject("JamesGleick")
gleick.set_height(5)
gleick.next_to(ORIGIN, RIGHT)
gleick.to_edge(DOWN)
quote.to_edge(UP)
self.play(
FadeInFrom(book, RIGHT),
FadeInFrom(gleick, LEFT),
)
self.wait()
self.play(Write(quote))
self.play(Write(
quote.power_part.copy().set_color(BLUE),
run_time=1
))
self.wait()
class WritePhaseFlow(Scene):
def construct(self):
words = TextMobject("Phase flow")
words.scale(2)
words.shift(FRAME_WIDTH * LEFT / 4)
words.to_edge(UP)
words.add_background_rectangle()
self.play(Write(words))
self.wait()
class ShowSineValues(Scene):
def construct(self):
angle_tracker = ValueTracker(60 * DEGREES)
get_angle = angle_tracker.get_value
formula = always_redraw(
lambda: self.get_sine_formula(get_angle())
)
self.add(formula)
self.play(
angle_tracker.set_value, 0,
run_time=3,
)
self.wait()
self.play(
angle_tracker.set_value, 90 * DEGREES,
run_time=3,
)
self.wait()
def get_sine_formula(self, angle):
sin, lp, rp = TexMobject(
"\\sin", "(", ") = "
)
input_part = Integer(
angle / DEGREES,
unit="^\\circ",
)
input_part.set_color(YELLOW)
output_part = DecimalNumber(
np.sin(input_part.get_value() * DEGREES),
num_decimal_places=3,
)
result = VGroup(
sin, lp, input_part, rp, output_part
)
result.arrange(RIGHT, buff=SMALL_BUFF)
sin.scale(1.1, about_edge=DOWN)
lp.align_to(rp, UP)
return result
class SetAsideSeekingSolution(Scene):
def construct(self):
ode = get_ode()
ode.to_edge(UP)
q1 = TextMobject("Find an exact solution")
q1.set_color(YELLOW)
q2 = TexMobject(
"\\text{What is }", "\\theta", "(t)",
"\\text{'s personality?}",
tex_to_color_map={"\\theta": BLUE},
arg_separator="",
)
theta = q2.get_part_by_tex("\\theta")
for q in q1, q2:
q.scale(1.5)
q.next_to(ode, DOWN, MED_LARGE_BUFF)
eyes = Eyes(theta, height=0.1)
self.add(ode)
self.add(q1)
self.wait()
self.play(
q1.scale, 0.3,
q1.to_corner, UR, MED_SMALL_BUFF,
)
self.play(FadeInFrom(q2, DOWN))
self.play(
eyes.blink,
rate_func=lambda t: smooth(1 - t),
)
self.play(eyes.look_at, q2.get_left())
self.play(eyes.look_at, q2.get_right())
self.play(
eyes.blink,
rate_func=squish_rate_func(there_and_back)
)
self.wait()
self.play(
eyes.change_mode, "confused",
eyes.look_at, ode.get_left(),
)
self.play(
eyes.blink,
rate_func=squish_rate_func(there_and_back)
)
class ThreeBodyTitle(Scene):
def construct(self):
title = TextMobject("Three body problem")
title.scale(1.5)
title.to_edge(UP)
self.add(title)
class ThreeBodySymbols(Scene):
def construct(self):
self.init_coord_groups()
self.introduce_coord_groups()
self.count_coords()
def init_coord_groups(self):
kwargs = {
"bracket_v_buff": 2 * SMALL_BUFF
}
positions = VGroup(*[
get_vector_symbol(*[
"{}_{}".format(s, i)
for s in "xyz"
], **kwargs)
for i in range(1, 4)
])
velocities = VGroup(*[
get_vector_symbol(*[
"p^{}_{}".format(s, i)
for s in "xyz"
], **kwargs)
for i in range(1, 4)
])
groups = VGroup(positions, velocities)
colors = [GREEN, RED, BLUE]
for group in groups:
for matrix in group:
matrix.coords = matrix.get_entries()
for coord, color in zip(matrix.coords, colors):
coord.set_color(color)
group.arrange(RIGHT)
groups.arrange(DOWN, buff=LARGE_BUFF)
groups.to_edge(LEFT)
self.coord_groups = groups
def introduce_coord_groups(self):
groups = self.coord_groups
x_group, p_group = groups
x_word = TextMobject("Positions")
p_word = TextMobject("Momenta")
words = VGroup(x_word, p_word)
for word, group in zip(words, groups):
word.next_to(group, UP)
rect_groups = VGroup()
for group in groups:
rect_group = VGroup(*[
SurroundingRectangle(
VGroup(*[
tm.coords[i]
for tm in group
]),
color=WHITE,
stroke_width=2,
)
for i in range(3)
])
rect_groups.add(rect_group)
self.play(
*[
LaggedStartMap(
FadeInFrom, group,
lambda m: (m, UP),
run_time=1,
)
for group in groups
],
*map(FadeInFromDown, words),
)
for rect_group in rect_groups:
self.play(
ShowCreationThenFadeOut(
rect_group,
lag_ratio=0.5,
)
)
self.wait()
def count_coords(self):
coord_copies = VGroup()
for group in self.coord_groups:
for tex_mob in group:
for coord in tex_mob.coords:
coord_copy = coord.copy()
coord_copy.set_stroke(
WHITE, 2, background=True
)
coord_copies.add(coord_copy)
count = Integer()
count_word = TextMobject("18", "degrees \\\\ of freedom")[1]
count_group = VGroup(count, count_word)
count_group.arrange(
RIGHT,
aligned_edge=DOWN,
)
count_group.scale(1.5)
count_group.next_to(
self.coord_groups, RIGHT,
aligned_edge=DOWN,
)
count.add_updater(
lambda m: m.set_value(len(coord_copies))
)
count.add_updater(
lambda m: m.next_to(count_word[0][0], LEFT, aligned_edge=DOWN)
)
self.add(count_group)
self.play(
# ChangeDecimalToValue(count, len(coord_copies)),
ShowIncreasingSubsets(coord_copies),
run_time=1.5,
rate_func=linear,
)
self.play(FadeOut(coord_copies))
class ThreeBodyEquation(Scene):
def construct(self):
x1 = "\\vec{\\textbf{x}}_1"
x2 = "\\vec{\\textbf{x}}_2"
x3 = "\\vec{\\textbf{x}}_3"
kw = {
"tex_to_color_map": {
x1: RED,
x2: GREEN,
x3: BLUE,
}
}
equations = VGroup(*[
TexMobject(
"{d^2", t1, "\\over dt^2}", "=",
"G", "\\left("
"{" + m2, "(", t2, "-", t1, ")"
"\\over"
"||", t2, "-", t1, "||^3}",
"+",
"{" + m3, "(", t3, "-", t1, ")"
"\\over"
"||", t3, "-", t1, "||^3}",
"\\right)",
**kw
)
for t1, t2, t3, m1, m2, m3 in [
(x1, x2, x3, "m_1", "m_2", "m_3"),
(x2, x3, x1, "m_2", "m_3", "m_1"),
(x3, x1, x2, "m_3", "m_1", "m_2"),
]
])
equations.arrange(DOWN, buff=LARGE_BUFF)
self.play(LaggedStartMap(
FadeInFrom, equations,
lambda m: (m, UP),
lag_ratio=0.2,
))
self.wait()
class JumpToThisPoint(Scene):
def construct(self):
dot = Dot(color=YELLOW)
dot.scale(0.5)
arrow = Vector(DR, color=WHITE)
arrow.next_to(dot, UL, SMALL_BUFF)
words = TextMobject(
"Jump directly to\\\\",
"this point?",
)
words.add_background_rectangle_to_submobjects()
words.next_to(arrow.get_start(), UP, SMALL_BUFF)
self.play(
FadeInFromLarge(dot, 20),
rate_func=rush_into,
)
self.play(
GrowArrow(arrow),
FadeInFromDown(words),
)
class ChaosTitle(Scene):
def construct(self):
title = TextMobject("Chaos theory")
title.scale(1.5)
title.to_edge(UP)
line = Line(LEFT, RIGHT)
line.set_width(FRAME_WIDTH - 1)
line.next_to(title, DOWN)
self.play(
Write(title),
ShowCreation(line),
)
self.wait()
class RevisitQuote(StrogatzQuote, PiCreatureScene):
def construct(self):
quote = self.get_quote()
quote.law_part.set_color(YELLOW)
quote.language_part.set_color(BLUE)
quote.set_stroke(BLACK, 6, background=True)
quote.scale(0.8, about_edge=UL)
new_langauge_part = TextMobject(
"\\Large Language of differential equations"
)
new_langauge_part.to_edge(UP)
new_langauge_part.match_style(quote.language_part)
randy = self.pi_creature
self.play(
FadeInFrom(quote[:-1], DOWN),
FadeInFrom(quote[-1:], LEFT),
randy.change, "raise_right_hand",
)
self.play(Blink(randy))
self.play(randy.change, "angry")
self.play(
Blink(randy),
VFadeOut(randy, run_time=3)
)
mover = VGroup(quote.language_part)
self.add(quote, mover)
self.play(
ReplacementTransform(
mover, new_langauge_part,
),
*[
FadeOut(part)
for part in quote
if part is not quote.language_part
],
run_time=2,
)
self.wait()
class EndScreen(PatreonEndScreen):
CONFIG = {
"specific_patrons": [
"Juan Benet",
"Vassili Philippov",
"Burt Humburg",
"Carlos Vergara Del Rio",
"Matt Russell",
"Scott Gray",
"soekul",
"Tihan Seale",
"Ali Yahya",
"dave nicponski",
"Evan Phillips",
"Graham",
"Joseph Kelly",
"Kaustuv DeBiswas",
"LambdaLabs",
"Lukas Biewald",
"Mike Coleman",
"Peter Mcinerney",
"Quantopian",
"Roy Larson",
"Scott Walter, Ph.D.",
"Yana Chernobilsky",
"Yu Jun",
"Jordan Scales",
"Lukas -krtek.net- Novy",
"John Shaughnessy",
"Britt Selvitelle",
"David Gow",
"J",
"Jonathan Wilson",
"Joseph John Cox",
"Magnus Dahlström",
"Randy C. Will",
"Ryan Atallah",
"Luc Ritchie",
"1stViewMaths",
"Adrian Robinson",
"Alexis Olson",
"Andreas Benjamin Brössel",
"Andrew Busey",
"Ankalagon",
"Antonio Juarez",
"Arjun Chakroborty",
"Art Ianuzzi",
"Awoo",
"Bernd Sing",
"Boris Veselinovich",
"Brian Staroselsky",
"Chad Hurst",
"Charles Southerland",
"Chris Connett",
"Christian Kaiser",
"Clark Gaebel",
"Cooper Jones",
"Danger Dai",
"Dave B",
"Dave Kester",
"David Clark",
"DeathByShrimp",
"Delton Ding",
"eaglle",
"emptymachine",
"Eric Younge",
"Eryq Ouithaqueue",
"Federico Lebron",
"Giovanni Filippi",
"Hal Hildebrand",
"Herman Dieset",
"Hitoshi Yamauchi",
"Isaac Jeffrey Lee",
"j eduardo perez",
"Jacob Magnuson",
"Jameel Syed",
"Jason Hise",
"Jeff Linse",
"Jeff Straathof",
"John Griffith",
"John Haley",
"John V Wertheim",
"Jonathan Eppele",
"Kai-Siang Ang",
"Kanan Gill",
"L0j1k",
"Lee Redden",
"Linh Tran",
"Ludwig Schubert",
"Magister Mugit",
"Mark B Bahu",
"Mark Heising",
"Martin Price",
"Mathias Jansson",
"Matt Langford",
"Matt Roveto",
"Matthew Cocke",
"Michael Faust",
"Michael Hardel",
"Mirik Gogri",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nero Li",
"Nikita Lesnikov",
"Omar Zrien",
"Owen Campbell-Moore",
"Peter Ehrnstrom",
"RedAgent14",
"rehmi post",
"Richard Burgmann",
"Richard Comish",
"Ripta Pasay",
"Rish Kundalia",
"Robert Teed",
"Roobie",
"Ryan Williams",
"Samuel D. Judge",
"Solara570",
"Stevie Metke",
"Tal Einav",
"Ted Suzman",
"Valeriy Skobelev",
"Xavier Bernard",
"Yavor Ivanov",
"Yaw Etse",
"YinYangBalance.Asia",
"Zach Cardwell",
]
}

View file

@ -1,931 +0,0 @@
from manimlib.imports import *
# import scipy
class FourierCirclesScene(Scene):
CONFIG = {
"n_vectors": 10,
"big_radius": 2,
"colors": [
BLUE_D,
BLUE_C,
BLUE_E,
GREY_BROWN,
],
"circle_style": {
"stroke_width": 2,
},
"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,
},
"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_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_vectors)]
def get_color_iterator(self):
return it.cycle(self.colors)
def get_rotating_vectors(self, freqs=None, coefficients=None):
vectors = VGroup()
self.center_tracker = VectorizedPoint(self.center_point)
if freqs is None:
freqs = self.get_freqs()
if coefficients is None:
coefficients = self.get_coefficients()
last_vector = None
for freq, coefficient in zip(freqs, coefficients):
if last_vector:
center_func = last_vector.get_end
else:
center_func = self.center_tracker.get_location
vector = self.get_rotating_vector(
coefficient=coefficient,
freq=freq,
center_func=center_func,
)
vectors.add(vector)
last_vector = vector
return vectors
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:
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
)
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):
circle.set_width(2 * circle.radius_func())
circle.move_to(circle.center_func())
return circle
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, [
complex_to_R3(
coef * np.exp(TAU * 1j * freq * t)
)
for coef, freq in zip(coefs, freqs)
]),
t_min=0,
t_max=1,
color=color,
step_size=self.parametric_function_step_size,
)
return path
# TODO, this should be a general animated mobect
def get_drawn_path_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 = self.get_drawn_path_alpha()
n_curves = len(path)
for a, sp in zip(np.linspace(0, 1, n_curves), path):
b = alpha - a
if b < 0:
width = 0
else:
width = stroke_width * (1 - (b % 1))
sp.set_stroke(width=width)
path.curr_time += dt
return path
broken_path.set_color(self.drawn_path_color)
broken_path.add_updater(update_path)
return broken_path
def get_y_component_wave(self,
vectors,
left_x=1,
color=PINK,
n_copies=2,
right_shift_rate=5):
path = self.get_vector_sum_path(vectors)
wave = ParametricFunction(
lambda t: op.add(
right_shift_rate * t * LEFT,
path.function(t)[1] * UP
),
t_min=path.t_min,
t_max=path.t_max,
color=color,
)
wave_copies = VGroup(*[
wave.copy()
for x in range(n_copies)
])
wave_copies.arrange(RIGHT, buff=0)
top_point = wave_copies.get_top()
wave.creation = ShowCreation(
wave,
run_time=(1 / self.get_slow_factor()),
rate_func=linear,
)
cycle_animation(wave.creation)
wave.add_updater(lambda m: m.shift(
(m.get_left()[0] - left_x) * LEFT
))
def update_wave_copies(wcs):
index = int(
wave.creation.total_time * self.get_slow_factor()
)
wcs[:index].match_style(wave)
wcs[index:].set_stroke(width=0)
wcs.next_to(wave, RIGHT, buff=0)
wcs.align_to(top_point, UP)
wave_copies.add_updater(update_wave_copies)
return VGroup(wave, wave_copies)
def get_wave_y_line(self, vectors, wave):
return DashedLine(
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()
dt = 1 / n_samples
ts = np.arange(0, 1, dt)
samples = np.array([
path.point_from_proportion(t)
for t in ts
])
samples -= self.center_point
complex_samples = samples[:, 0] + 1j * samples[:, 1]
result = []
for freq in freqs:
riemann_sum = np.array([
np.exp(-TAU * 1j * freq * t) * cs
for t, cs in zip(ts, complex_samples)
]).sum() * dt
result.append(riemann_sum)
return result
class FourierSeriesIntroBackground4(FourierCirclesScene):
CONFIG = {
"n_vectors": 4,
"center_point": 4 * LEFT,
"run_time": 30,
"big_radius": 1.5,
}
def construct(self):
circles = self.get_circles()
path = self.get_drawn_path(circles)
wave = self.get_y_component_wave(circles)
h_line = always_redraw(
lambda: self.get_wave_y_line(circles, wave)
)
# Why?
circles.update(-1 / self.camera.frame_rate)
#
self.add(circles, path, wave, h_line)
self.wait(self.run_time)
def get_ks(self):
return np.arange(1, 2 * self.n_vectors + 1, 2)
def get_freqs(self):
return self.base_frequency * self.get_ks()
def get_coefficients(self):
return self.big_radius / self.get_ks()
class FourierSeriesIntroBackground8(FourierSeriesIntroBackground4):
CONFIG = {
"n_vectors": 8,
}
class FourierSeriesIntroBackground12(FourierSeriesIntroBackground4):
CONFIG = {
"n_vectors": 12,
}
class FourierSeriesIntroBackground20(FourierSeriesIntroBackground4):
CONFIG = {
"n_vectors": 20,
}
class FourierOfPiSymbol(FourierCirclesScene):
CONFIG = {
"n_vectors": 51,
"center_point": ORIGIN,
"slow_factor": 0.1,
"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)
vectors = self.get_rotating_vectors(coefficients=coefs)
circles = self.get_circles(vectors)
self.set_decreasing_stroke_widths(circles)
# approx_path = self.get_vector_sum_path(circles)
drawn_path = self.get_drawn_path(vectors)
if self.start_drawn:
self.vector_clock.increment_value(1)
self.add(path)
self.add(vectors)
self.add(circles)
self.add(drawn_path)
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(
# mcsw / np.sqrt(k),
mcsw / k,
mcsw,
))
return circles
def get_path(self):
tex_mob = TexMobject(self.tex)
tex_mob.set_height(6)
path = tex_mob.family_members_with_points()[0]
path.set_fill(opacity=0)
path.set_stroke(WHITE, 1)
return path
class FourierOfTexPaths(FourierOfPiSymbol, MovingCameraScene):
CONFIG = {
"n_vectors": 100,
"name_color": WHITE,
"animated_name": "Abc",
"time_per_symbol": 5,
"slow_factor": 1 / 5,
"parametric_function_step_size": 0.01,
}
def construct(self):
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_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_vectors)
drawn_path.clear_updaters()
drawn_path.set_stroke(self.name_color, 3)
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
)
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_vectors": 5,
"run_time": 10,
}
class FourierOfTrebleClef(FourierOfPiSymbol):
CONFIG = {
"n_vectors": 101,
"run_time": 10,
"start_drawn": True,
"file_name": "TrebleClef",
"height": 7.5,
}
def get_shape(self):
shape = SVGMobject(self.file_name)
return shape
def get_path(self):
shape = self.get_shape()
path = shape.family_members_with_points()[0]
path.set_height(self.height)
path.set_fill(opacity=0)
path.set_stroke(WHITE, 0)
return path
class FourierOfIP(FourierOfTrebleClef):
CONFIG = {
"file_name": "IP_logo2",
"height": 6,
"n_vectors": 100,
}
# def construct(self):
# path = self.get_path()
# self.add(path)
def get_shape(self):
shape = SVGMobject(self.file_name)
return shape
def get_path(self):
shape = self.get_shape()
path = shape.family_members_with_points()[0]
path.add_line_to(path.get_start())
# path.make_smooth()
path.set_height(self.height)
path.set_fill(opacity=0)
path.set_stroke(WHITE, 0)
return path
class FourierOfEighthNote(FourierOfTrebleClef):
CONFIG = {
"file_name": "EighthNote"
}
class FourierOfN(FourierOfTrebleClef):
CONFIG = {
"height": 6,
"n_vectors": 1000,
}
def get_shape(self):
return TexMobject("N")
class FourierNailAndGear(FourierOfTrebleClef):
CONFIG = {
"height": 6,
"n_vectors": 200,
"run_time": 100,
"slow_factor": 0.01,
"parametric_function_step_size": 0.0001,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
shape = SVGMobject("Nail_And_Gear")[1]
return shape
class FourierBatman(FourierOfTrebleClef):
CONFIG = {
"height": 4,
"n_vectors": 100,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
shape = SVGMobject("BatmanLogo")[1]
return shape
class FourierHeart(FourierOfTrebleClef):
CONFIG = {
"height": 4,
"n_vectors": 100,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
shape = SuitSymbol("hearts")
return shape
def get_drawn_path(self, *args, **kwargs):
kwargs["stroke_width"] = 5
path = super().get_drawn_path(*args, **kwargs)
path.set_color(PINK)
return path
class FourierNDQ(FourierOfTrebleClef):
CONFIG = {
"height": 4,
"n_vectors": 1000,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
path = VMobject()
shape = TexMobject("NDQ")
for sp in shape.family_members_with_points():
path.append_points(sp.points)
return path
class FourierGoogleG(FourierOfTrebleClef):
CONFIG = {
"n_vectors": 10,
"height": 5,
"g_colors": [
"#4285F4",
"#DB4437",
"#F4B400",
"#0F9D58",
]
}
def get_shape(self):
g = SVGMobject("google_logo")[5]
g.center()
self.add(g)
return g
def get_drawn_path(self, *args, **kwargs):
kwargs["stroke_width"] = 7
path = super().get_drawn_path(*args, **kwargs)
blue, red, yellow, green = self.g_colors
path[:250].set_color(blue)
path[250:333].set_color(green)
path[333:370].set_color(yellow)
path[370:755].set_color(red)
path[755:780].set_color(yellow)
path[780:860].set_color(green)
path[860:].set_color(blue)
return path
class ExplainCircleAnimations(FourierCirclesScene):
CONFIG = {
"n_vectors": 100,
"center_point": 2 * DOWN,
"n_top_circles": 9,
"path_height": 3,
}
def construct(self):
self.add_path()
self.add_circles()
self.wait(8)
self.organize_circles_in_a_row()
self.show_frequencies()
self.show_examples_for_frequencies()
self.show_as_vectors()
self.show_vector_sum()
self.tweak_starting_vectors()
def add_path(self):
self.path = self.get_path()
self.add(self.path)
def add_circles(self):
coefs = self.get_coefficients_of_path(self.path)
self.circles = self.get_circles(coefficients=coefs)
self.add(self.circles)
self.drawn_path = self.get_drawn_path(self.circles)
self.add(self.drawn_path)
def organize_circles_in_a_row(self):
circles = self.circles
top_circles = circles[:self.n_top_circles].copy()
center_trackers = VGroup()
for circle in top_circles:
tracker = VectorizedPoint(circle.center_func())
circle.center_func = tracker.get_location
center_trackers.add(tracker)
tracker.freq = circle.freq
tracker.circle = circle
center_trackers.submobjects.sort(
key=lambda m: m.freq
)
center_trackers.generate_target()
right_buff = 1.45
center_trackers.target.arrange(RIGHT, buff=right_buff)
center_trackers.target.to_edge(UP, buff=1.25)
self.add(top_circles)
self.play(
MoveToTarget(center_trackers),
run_time=2
)
self.wait(4)
self.top_circles = top_circles
self.center_trackers = center_trackers
def show_frequencies(self):
center_trackers = self.center_trackers
freq_numbers = VGroup()
for ct in center_trackers:
number = Integer(ct.freq)
number.next_to(ct, DOWN, buff=1)
freq_numbers.add(number)
ct.circle.number = number
ld, rd = [
TexMobject("\\dots")
for x in range(2)
]
ld.next_to(freq_numbers, LEFT, MED_LARGE_BUFF)
rd.next_to(freq_numbers, RIGHT, MED_LARGE_BUFF)
freq_numbers.add_to_back(ld)
freq_numbers.add(rd)
freq_word = TextMobject("Frequencies")
freq_word.scale(1.5)
freq_word.set_color(YELLOW)
freq_word.next_to(freq_numbers, DOWN, MED_LARGE_BUFF)
self.play(
LaggedStartMap(
FadeInFromDown, freq_numbers
)
)
self.play(
Write(freq_word),
LaggedStartMap(
ShowCreationThenFadeAround, freq_numbers,
)
)
self.wait(2)
self.freq_numbers = freq_numbers
self.freq_word = freq_word
def show_examples_for_frequencies(self):
top_circles = self.top_circles
c1, c2, c3 = [
list(filter(
lambda c: c.freq == k,
top_circles
))[0]
for k in (1, 2, 3)
]
neg_circles = VGroup(*filter(
lambda c: c.freq < 0,
top_circles
))
for c in [c1, c2, c3, *neg_circles]:
c.rect = SurroundingRectangle(c)
self.play(
ShowCreation(c2.rect),
WiggleOutThenIn(c2.number),
)
self.wait(2)
self.play(
ReplacementTransform(c2.rect, c1.rect),
)
self.play(FadeOut(c1.rect))
self.wait()
self.play(
ShowCreation(c3.rect),
WiggleOutThenIn(c3.number),
)
self.play(
FadeOut(c3.rect),
)
self.wait(2)
self.play(
LaggedStart(*[
ShowCreationThenFadeOut(c.rect)
for c in neg_circles
])
)
self.wait(3)
self.play(FadeOut(self.freq_word))
def show_as_vectors(self):
top_circles = self.top_circles
top_vectors = self.get_rotating_vectors(top_circles)
top_vectors.set_color(WHITE)
original_circles = top_circles.copy()
self.play(
FadeIn(top_vectors),
top_circles.set_opacity, 0,
)
self.wait(3)
self.play(
top_circles.match_style, original_circles
)
self.remove(top_vectors)
self.top_vectors = top_vectors
def show_vector_sum(self):
trackers = self.center_trackers.copy()
trackers.sort(
submob_func=lambda t: abs(t.circle.freq - 0.1)
)
plane = self.plane = NumberPlane(
x_min=-3,
x_max=3,
y_min=-2,
y_max=2,
axis_config={
"stroke_color": LIGHT_GREY,
}
)
plane.set_stroke(width=1)
plane.fade(0.5)
plane.move_to(self.center_point)
self.play(
FadeOut(self.drawn_path),
FadeOut(self.circles),
self.slow_factor_tracker.set_value, 0.05,
)
self.add(plane, self.path)
self.play(FadeIn(plane))
new_circles = VGroup()
last_tracker = None
for tracker in trackers:
if last_tracker:
tracker.new_location_func = last_tracker.circle.get_start
else:
tracker.new_location_func = lambda: self.center_point
original_circle = tracker.circle
tracker.circle = original_circle.copy()
tracker.circle.center_func = tracker.get_location
new_circles.add(tracker.circle)
self.add(tracker, tracker.circle)
start_point = tracker.get_location()
self.play(
UpdateFromAlphaFunc(
tracker, lambda t, a: t.move_to(
interpolate(
start_point,
tracker.new_location_func(),
a,
)
),
run_time=2
)
)
tracker.add_updater(lambda t: t.move_to(
t.new_location_func()
))
self.wait(2)
last_tracker = tracker
self.wait(3)
self.clear()
self.slow_factor_tracker.set_value(0.1)
self.add(
self.top_circles,
self.freq_numbers,
self.path,
)
self.add_circles()
for tc in self.top_circles:
for c in self.circles:
if c.freq == tc.freq:
tc.rotate(
angle_of_vector(c.get_start() - c.get_center()) -
angle_of_vector(tc.get_start() - tc.get_center())
)
self.wait(10)
def tweak_starting_vectors(self):
top_circles = self.top_circles
circles = self.circles
path = self.path
drawn_path = self.drawn_path
new_path = self.get_new_path()
new_coefs = self.get_coefficients_of_path(new_path)
new_circles = self.get_circles(coefficients=new_coefs)
new_top_circles = VGroup()
new_top_vectors = VGroup()
for top_circle in top_circles:
for circle in new_circles:
if circle.freq == top_circle.freq:
new_top_circle = circle.copy()
new_top_circle.center_func = top_circle.get_center
new_top_vector = self.get_rotating_vector(
new_top_circle
)
new_top_circles.add(new_top_circle)
new_top_vectors.add(new_top_vector)
self.play(
self.slow_factor_tracker.set_value, 0,
FadeOut(drawn_path)
)
self.wait()
self.play(
ReplacementTransform(top_circles, new_top_circles),
ReplacementTransform(circles, new_circles),
FadeOut(path),
run_time=3,
)
new_drawn_path = self.get_drawn_path(
new_circles, stroke_width=4,
)
self.add(new_drawn_path)
self.slow_factor_tracker.set_value(0.1)
self.wait(20)
#
def configure_path(self, path):
path.set_stroke(WHITE, 1)
path.set_fill(BLACK, opacity=1)
path.set_height(self.path_height)
path.move_to(self.center_point)
return path
def get_path(self):
tex = TexMobject("f")
path = tex.family_members_with_points()[0]
self.configure_path(path)
return path
# return Square().set_height(3)
def get_new_path(self):
shape = SVGMobject("TrebleClef")
path = shape.family_members_with_points()[0]
self.configure_path(path)
path.scale(1.5, about_edge=DOWN)
return path

File diff suppressed because it is too large Load diff

View file

@ -1,142 +0,0 @@
from manimlib.imports import *
from active_projects.diffyq.part2.wordy_scenes import WriteHeatEquationTemplate
class ReactionsToInitialHeatEquation(PiCreatureScene):
def construct(self):
randy = self.pi_creature
randy.set_color(BLUE_C)
randy.center()
point = VectorizedPoint().next_to(randy, UL, LARGE_BUFF)
randy.add_updater(lambda r: r.look_at(point))
self.play(randy.change, "horrified")
self.wait()
self.play(randy.change, "pondering")
self.wait()
self.play(
randy.change, "confused",
point.next_to, randy, UR, LARGE_BUFF,
)
self.wait(2)
self.play(
point.shift, 2 * DOWN,
randy.change, "horrified"
)
self.wait(4)
class ContrastPDEToODE(TeacherStudentsScene):
CONFIG = {
"random_seed": 2,
}
def construct(self):
student = self.students[2]
pde, ode = words = VGroup(*[
TextMobject(
text + "\\\\",
"Differential\\\\",
"Equation"
)
for text in ("Partial", "Ordinary")
])
pde[0].set_color(YELLOW)
ode[0].set_color(BLUE)
for word in words:
word.arrange(DOWN, aligned_edge=LEFT)
words.arrange(RIGHT, buff=LARGE_BUFF)
words.next_to(student.get_corner(UR), UP, MED_LARGE_BUFF)
words.shift(UR)
lt = TexMobject("<")
lt.scale(1.5)
lt.move_to(Line(pde.get_right(), ode.get_left()))
for pi in self.pi_creatures:
pi.add_updater(lambda p: p.look_at(pde))
self.play(
FadeInFromDown(VGroup(words, lt)),
student.change, "raise_right_hand",
)
self.play(
self.get_student_changes("pondering", "pondering", "hooray"),
self.teacher.change, "happy"
)
self.wait(3)
self.play(
Swap(ode, pde),
self.teacher.change, "raise_right_hand",
self.get_student_changes(
"erm", "sassy", "confused"
)
)
self.look_at(words)
self.change_student_modes(
"thinking", "thinking", "tease",
)
self.wait(3)
class AskAboutWhereEquationComesFrom(TeacherStudentsScene, WriteHeatEquationTemplate):
def construct(self):
equation = self.get_d1_equation()
equation.move_to(self.hold_up_spot, DOWN)
self.play(
FadeInFromDown(equation),
self.teacher.change, "raise_right_hand"
)
self.student_says(
"Um...why?",
target_mode="sassy",
student_index=2,
bubble_kwargs={"direction": RIGHT},
)
self.change_student_modes(
"confused", "confused", "sassy",
)
self.wait()
self.play(
self.teacher.change, "pondering",
)
self.wait(2)
class AskWhyRewriteIt(TeacherStudentsScene):
def construct(self):
self.student_says(
"Why?", student_index=1,
bubble_kwargs={"height": 2, "width": 2},
)
self.students[1].bubble = None
self.teacher_says(
"One step closer\\\\to derivatives"
)
self.change_student_modes(
"thinking", "thinking", "thinking",
look_at_arg=4 * LEFT + 2 * UP
)
self.wait(2)
class ReferenceKhanVideo(TeacherStudentsScene):
def construct(self):
khan_logo = ImageMobject("KhanLogo")
khan_logo.set_height(1)
khan_logo.next_to(self.teacher, UP, buff=2)
khan_logo.shift(2 * LEFT)
self.play(
self.teacher.change, "raise_right_hand",
)
self.change_student_modes(
"thinking", "pondering", "thinking",
look_at_arg=self.screen
)
self.wait()
self.play(FadeInFromDown(khan_logo))
self.look_at(self.screen)
self.wait(15)

View file

@ -1,35 +0,0 @@
from manimlib.imports import *
TIME_COLOR = YELLOW
X_COLOR = GREEN
def get_heat_equation():
pass
def temperature_to_color(temp, min_temp=-1, max_temp=1):
colors = [BLUE, TEAL, GREEN, YELLOW, "#ff0000"]
alpha = inverse_interpolate(min_temp, max_temp, temp)
index, sub_alpha = integer_interpolate(
0, len(colors) - 1, alpha
)
return interpolate_color(
colors[index], colors[index + 1], sub_alpha
)
def two_d_temp_func(x, y, t):
return np.sum([
c * np.sin(f * var) * np.exp(-(f**2) * t)
for c, f, var in [
(0.2, 1, x),
(0.3, 3, x),
(0.02, 5, x),
(0.01, 7, x),
(0.5, 2, y),
(0.1, 10, y),
(0.01, 20, y),
]
])

View file

@ -1,794 +0,0 @@
from manimlib.imports import *
from active_projects.diffyq.part1.staging import TourOfDifferentialEquations
class PartTwoOfTour(TourOfDifferentialEquations):
CONFIG = {
"zoomed_thumbnail_index": 1,
}
def construct(self):
self.add_title()
self.show_thumbnails()
self.zoom_in_to_one_thumbnail()
def zoom_in_to_one_thumbnail(self):
frame = self.camera_frame
thumbnails = self.thumbnails
ode = TextMobject("Ordinary\\\\", "Differential Equation")
pde = TextMobject("Partial\\\\", "Differential Equation")
for word, thumbnail, vect in zip([ode, pde], thumbnails, [DOWN, UP]):
word.match_width(thumbnail)
word.next_to(thumbnail, vect)
ode[0].set_color(BLUE)
pde[0].set_color(YELLOW)
self.add(ode)
frame.save_state()
self.play(
frame.replace,
thumbnails[0],
run_time=1,
)
self.play(
Restore(frame, run_time=3),
)
self.play(
TransformFromCopy(ode, pde),
)
self.play(
ApplyMethod(
frame.replace, thumbnails[1],
path_arc=(-30 * DEGREES),
run_time=3
),
)
self.wait()
class BrownianMotion(Scene):
CONFIG = {
"wait_time": 60,
"L": 3, # Box in [-L, L] x [-L, L]
"n_particles": 100,
"m1": 1,
"m2": 100,
"r1": 0.05,
"r2": 0.5,
"max_v": 5,
"random_seed": 2,
}
def construct(self):
self.add_title()
self.add_particles()
self.wait(self.wait_time)
def add_title(self):
square = Square(side_length=2 * self.L)
title = TextMobject("Brownian motion")
title.scale(1.5)
title.next_to(square, UP)
self.add(square)
self.add(title)
def add_particles(self):
m1 = self.m1
m2 = self.m2
r1 = self.r1
r2 = self.r2
L = self.L
max_v = self.max_v
n_particles = self.n_particles
lil_particles = VGroup(*[
self.get_particle(m1, r1, L, max_v)
for k in range(n_particles)
])
big_particle = self.get_particle(m2, r2, L=r2, max_v=0)
big_particle.set_fill(YELLOW, 1)
for p in lil_particles:
if self.are_colliding(p, big_particle):
lil_particles.remove(p)
all_particles = VGroup(big_particle, *lil_particles)
all_particles.add_updater(self.update_particles)
path = self.get_traced_path(big_particle)
self.add(all_particles)
self.add(path)
self.particles = all_particles
self.big_particle = big_particle
self.path = path
def get_particle(self, m, r, L, max_v):
dot = Dot(radius=r)
dot.set_fill(WHITE, 0.7)
dot.mass = m
dot.radius = r
dot.center = op.add(
np.random.uniform(-L + r, L - r) * RIGHT,
np.random.uniform(-L + r, L - r) * UP
)
dot.move_to(dot.center)
dot.velocity = rotate_vector(
np.random.uniform(0, max_v) * RIGHT,
np.random.uniform(0, TAU),
)
return dot
def are_colliding(self, p1, p2):
d = get_norm(p1.get_center() - p2.get_center())
return (d < p1.radius + p2.radius)
def get_traced_path(self, particle):
path = VMobject()
path.set_stroke(BLUE, 3)
path.start_new_path(particle.get_center())
buff = 0.02
def update_path(path):
new_point = particle.get_center()
if get_norm(new_point - path.get_last_point()) > buff:
path.add_line_to(new_point)
path.add_updater(update_path)
return path
def update_particles(self, particles, dt):
for p1 in particles:
p1.center += p1.velocity * dt
# Check particle collisions
buff = 0.01
for p2 in particles:
if p1 is p2:
continue
v = p2.center - p1.center
dist = get_norm(v)
r_sum = p1.radius + p2.radius
diff = dist - r_sum
if diff < 0:
unit_v = v / dist
p1.center += (diff - buff) * unit_v / 2
p2.center += -(diff - buff) * unit_v / 2
u1 = p1.velocity
u2 = p2.velocity
m1 = p1.mass
m2 = p2.mass
v1 = (
(m2 * (u2 - u1) + m1 * u1 + m2 * u2) /
(m1 + m2)
)
v2 = (
(m1 * (u1 - u2) + m1 * u1 + m2 * u2) /
(m1 + m2)
)
p1.velocity = v1
p2.velocity = v2
# Check edge collisions
r1 = p1.radius
c1 = p1.center
for i in [0, 1]:
if abs(c1[i]) + r1 > self.L:
c1[i] = np.sign(c1[i]) * (self.L - r1)
p1.velocity[i] *= -1 * op.mul(
np.sign(p1.velocity[i]),
np.sign(c1[i])
)
for p in particles:
p.move_to(p.center)
return particles
class AltBrownianMotion(BrownianMotion):
CONFIG = {
"wait_time": 20,
"n_particles": 100,
"m2": 10,
}
class BlackScholes(AltBrownianMotion):
def construct(self):
# For some reason I'm amused by the thought
# Of this graph perfectly matching the Brownian
# Motion y-coordiante
self.add_title()
self.add_particles()
self.particles.set_opacity(0)
self.remove(self.path)
self.add_graph()
self.wait(self.wait_time)
def add_title(self):
title = TextMobject("Black-Scholes equations")
title.scale(1.5)
title.next_to(2 * UP, UP)
equation = TexMobject(
"{\\partial V \\over \\partial t}", "+",
"\\frac{1}{2} \\sigma^2 S^2",
"{\\partial^2 V \\over \\partial S^2}", "+",
"rS", "{\\partial V \\over \\partial S}",
"-rV", "=", "0",
)
equation.scale(0.8)
equation.next_to(title, DOWN)
self.add(title)
self.add(equation)
self.title = title
self.equation = equation
def add_graph(self):
axes = Axes(
x_min=-1,
x_max=20,
y_min=0,
y_max=10,
axis_config={
"unit_size": 0.5,
},
)
axes.set_height(4)
axes.move_to(DOWN)
def get_graph_point():
return axes.c2p(
self.get_time(),
5 + 2 * self.big_particle.get_center()[1]
)
graph = VMobject()
graph.match_style(self.path)
graph.start_new_path(get_graph_point())
graph.add_updater(
lambda g: g.add_line_to(get_graph_point())
)
self.add(axes)
self.add(graph)
class ContrastChapters1And2(Scene):
def construct(self):
c1_frame, c2_frame = frames = VGroup(*[
ScreenRectangle(height=3.5)
for x in range(2)
])
frames.arrange(RIGHT, buff=LARGE_BUFF)
c1_title, c2_title = titles = VGroup(
TextMobject("Chapter 1"),
TextMobject("Chapter 2"),
)
titles.scale(1.5)
ode, pde = des = VGroup(
TextMobject(
"Ordinary",
"Differential Equations\\\\",
"ODEs",
),
TextMobject(
"Partial",
"Differential Equations\\\\",
"PDEs",
),
)
ode[0].set_color(BLUE)
pde[0].set_color(YELLOW)
for de in des:
de[-1][0].match_color(de[0])
de[-1].scale(1.5, about_point=de.get_top())
for title, frame, de in zip(titles, frames, des):
title.next_to(frame, UP)
de.match_width(frame)
de.next_to(frame, DOWN)
lt = TexMobject("<")
lt.move_to(Line(ode.get_right(), pde.get_left()))
lt.scale(2, about_edge=UP)
c1_words = TextMobject(
"They're", "really\\\\", "{}",
"freaking", "hard\\\\",
"to", "solve\\\\",
)
c1_words.set_height(0.5 * c1_frame.get_height())
c1_words.move_to(c1_frame)
c2_words = TextMobject(
"They're", "really", "\\emph{really}\\\\",
"freaking", "hard\\\\",
"to", "solve\\\\",
)
c2_words.set_color_by_tex("\\emph", YELLOW)
c2_words.move_to(c2_frame)
edit_shift = MED_LARGE_BUFF * RIGHT
c2_edits = VGroup(
TextMobject("sometimes").next_to(
c2_words[1:3], UP,
aligned_edge=LEFT,
),
Line(
c2_words[1].get_left(),
c2_words[2].get_right(),
stroke_width=8,
),
TextMobject("not too").next_to(
c2_words[3], LEFT,
),
Line(
c2_words[3].get_left(),
c2_words[3].get_right(),
stroke_width=8,
),
)
c2_edits.set_color(RED)
c2_edits[2:].shift(edit_shift)
self.add(titles)
self.add(frames)
self.add(des)
self.wait()
self.play(LaggedStartMap(
FadeInFromDown, c1_words,
lag_ratio=0.1,
))
self.wait()
# self.play(FadeIn(ode))
self.play(
# TransformFromCopy(ode, pde),
TransformFromCopy(c1_words, c2_words),
Write(lt)
)
self.wait()
self.play(
Write(c2_edits[:2], run_time=1),
)
self.play(
c2_words[3:5].shift, edit_shift,
Write(c2_edits[2:]),
run_time=1,
)
self.wait()
class ShowCubeFormation(ThreeDScene):
CONFIG = {
"camera_config": {
"shading_factor": 1.0,
},
"color": False,
}
def construct(self):
light_source = self.camera.light_source
light_source.move_to(np.array([-6, -3, 6]))
cube = Cube(
side_length=4,
fill_color=GREY,
stroke_color=WHITE,
stroke_width=0.5,
)
cube.set_fill(opacity=1)
if self.color:
# cube[0].set_color(BLUE)
# cube[1].set_color(RED)
# for face in cube[2:]:
# face.set_color([BLUE, RED])
cube.color_using_background_image("VerticalTempGradient")
# light_source.next_to(cube, np.array([1, -1, 1]), buff=2)
cube_3d = cube.copy()
cube_2d = cube_3d.copy().stretch(0, 2)
cube_1d = cube_2d.copy().stretch(0, 1)
cube_0d = cube_1d.copy().stretch(0, 0)
cube.become(cube_0d)
self.set_camera_orientation(
phi=70 * DEGREES,
theta=-145 * DEGREES,
)
self.begin_ambient_camera_rotation(rate=0.05)
for target in [cube_1d, cube_2d, cube_3d]:
self.play(
Transform(cube, target, run_time=1.5)
)
self.wait(8)
class ShowCubeFormationWithColor(ShowCubeFormation):
CONFIG = {
"color": True,
}
class ShowRect(Scene):
CONFIG = {
"height": 1,
"width": 3,
}
def construct(self):
rect = Rectangle(
height=self.height,
width=self.width,
)
rect.set_color(YELLOW)
self.play(ShowCreationThenFadeOut(rect))
class ShowSquare(ShowRect):
CONFIG = {
"height": 1,
"width": 1,
}
class ShowHLine(Scene):
def construct(self):
line = Line(LEFT, RIGHT)
line.set_color(BLUE)
self.play(ShowCreationThenFadeOut(line))
class ShowCross(Scene):
def construct(self):
cross = Cross(Square())
cross.set_width(3)
cross.set_height(1, stretch=True)
self.play(ShowCreation(cross))
class TwoBodyEquations(Scene):
def construct(self):
kw = {
"tex_to_color_map": {
"x_1": LIGHT_GREY,
"y_1": LIGHT_GREY,
"x_2": BLUE,
"y_2": BLUE,
"=": WHITE,
}
}
equations = VGroup(
TexMobject(
"{d^2 x_1 \\over dt^2}",
"=",
"{x_2 - x_1 \\over m_1 \\left(",
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
"\\right)^{3/2}",
**kw
),
TexMobject(
"{d^2 y_1 \\over dt^2}",
"=",
"{y_2 - y_1 \\over m_1 \\left(",
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
"\\right)^{3/2}",
**kw
),
TexMobject(
"{d^2 x_2 \\over dt^2}",
"=",
"{x_1 - x_2 \\over m_2 \\left(",
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
"\\right)^{3/2}",
**kw
),
TexMobject(
"{d^2 y_2 \\over dt^2}",
"=",
"{y_1 - y_2 \\over m_2 \\left(",
"(x_2 - x_1)^2 + (y_2 - y_1)^2",
"\\right)^{3/2}",
**kw
),
)
equations.arrange(DOWN, buff=LARGE_BUFF)
equations.set_height(6)
equations.to_edge(LEFT)
variables = VGroup()
lhss = VGroup()
rhss = VGroup()
for equation in equations:
variable = equation[1]
lhs = equation[:4]
rhs = equation[4:]
variables.add(variable)
lhss.add(lhs)
rhss.add(rhs)
lhss_copy = lhss.copy()
for variable, lhs in zip(variables, lhss):
variable.save_state()
variable.match_height(lhs)
variable.scale(0.7)
variable.move_to(lhs, LEFT)
self.play(LaggedStart(*[
FadeInFrom(v, RIGHT)
for v in variables
]))
self.wait()
self.play(
LaggedStartMap(Restore, variables),
FadeIn(
lhss_copy,
remover=True,
lag_ratio=0.1,
run_time=2,
)
)
self.add(lhss)
self.wait()
self.play(LaggedStartMap(
FadeIn, rhss
))
self.wait()
self.play(
LaggedStart(*[
ShowCreationThenFadeAround(lhs[:3])
for lhs in lhss
])
)
self.wait()
self.play(
LaggedStartMap(
ShowCreationThenFadeAround,
rhss,
)
)
self.wait()
class LaplacianIntuition(SpecialThreeDScene):
CONFIG = {
"three_d_axes_config": {
"x_min": -5,
"x_max": 5,
"y_min": -5,
"y_max": 5,
},
"surface_resolution": 32,
}
def construct(self):
axes = self.get_axes()
axes.scale(0.5, about_point=ORIGIN)
self.set_camera_to_default_position()
self.begin_ambient_camera_rotation()
def func(x, y):
return np.array([
x, y,
2.7 + 0.5 * (np.sin(x) + np.cos(y)) -
0.025 * (x**2 + y**2)
])
surface_config = {
"u_min": -5,
"u_max": 5,
"v_min": -5,
"v_max": 5,
"resolution": self.surface_resolution,
}
# plane = ParametricSurface(
# lambda u, v: np.array([u, v, 0]),
# **surface_config
# )
# plane.set_stroke(WHITE, width=0.1)
# plane.set_fill(WHITE, opacity=0.1)
plane = Square(
side_length=10,
stroke_width=0,
fill_color=WHITE,
fill_opacity=0.1,
)
plane.center()
plane.set_shade_in_3d(True)
surface = ParametricSurface(
func, **surface_config
)
surface.set_stroke(BLUE, width=0.1)
surface.set_fill(BLUE, opacity=0.25)
self.add(axes, plane, surface)
point = VectorizedPoint(np.array([2, -2, 0]))
dot = Dot()
dot.set_color(GREEN)
dot.add_updater(lambda d: d.move_to(point))
line = always_redraw(lambda: DashedLine(
point.get_location(),
func(*point.get_location()[:2]),
background_image_file="VerticalTempGradient",
))
circle = Circle(radius=0.25)
circle.set_color(YELLOW)
circle.insert_n_curves(20)
circle.add_updater(lambda m: m.move_to(point))
circle.set_shade_in_3d(True)
surface_circle = always_redraw(
lambda: circle.copy().apply_function(
lambda p: func(*p[:2])
).shift(
0.02 * IN
).color_using_background_image("VerticalTempGradient")
)
self.play(FadeInFromLarge(dot))
self.play(ShowCreation(line))
self.play(TransformFromCopy(dot, circle))
self.play(
Transform(
circle.copy(),
surface_circle.copy().clear_updaters(),
remover=True,
)
)
self.add(surface_circle)
self.wait()
for vect in [4 * LEFT, DOWN, 4 * RIGHT, UP]:
self.play(
point.shift, vect,
run_time=3,
)
class StrogatzMention(PiCreatureScene):
def construct(self):
self.show_book()
self.show_motives()
self.show_pages()
def show_book(self):
morty = self.pi_creature
book = ImageMobject("InfinitePowers")
book.set_height(5)
book.to_edge(LEFT)
steve = ImageMobject("Strogatz_by_bricks")
steve.set_height(5)
steve.to_edge(LEFT)
name = TextMobject("Steven Strogatz")
name.match_width(steve)
name.next_to(steve, DOWN)
self.think(
"Hmm...many good\\\\lessons here...",
run_time=1
)
self.wait()
self.play(FadeInFromDown(steve))
self.wait()
self.play(
FadeInFrom(book, DOWN),
steve.shift, 4 * RIGHT,
RemovePiCreatureBubble(
morty, target_mode="thinking"
)
)
self.wait(3)
self.play(
FadeOut(steve),
FadeOut(morty),
)
self.book = book
def show_motives(self):
motives = VGroup(
TextMobject("1) Scratch and itch"),
TextMobject("2) Make people love math"),
)
motives.scale(1.5)
motives.arrange(
DOWN, LARGE_BUFF,
aligned_edge=LEFT,
)
motives.move_to(
Line(
self.book.get_right(),
FRAME_WIDTH * RIGHT / 2
)
)
motives.to_edge(UP)
for motive in motives:
self.play(FadeInFromDown(motive))
self.wait(2)
self.play(FadeOut(motives))
def show_pages(self):
book = self.book
pages = Group(*[
ImageMobject("IP_Sample_Page{}".format(i))
for i in range(1, 4)
])
for page in pages:
page.match_height(book)
page.next_to(book, RIGHT)
last_page = VectorizedPoint()
for page in pages:
self.play(
FadeOut(last_page),
FadeIn(page)
)
self.wait()
last_page = page
self.play(FadeOut(last_page))
def create_pi_creature(self):
return Mortimer().to_corner(DR)
class Thumbnail(Scene):
def construct(self):
image = ImageMobject("HeatSurfaceExampleFlipped")
image.set_height(6.5)
image.to_edge(DOWN, buff=-SMALL_BUFF)
self.add(image)
equation = TexMobject(
"{\\partial {T} \\over \\partial {t}}", "=",
"\\alpha", "\\nabla^2 {T}",
tex_to_color_map={
"{t}": YELLOW,
"{T}": RED,
}
)
equation.scale(2)
equation.to_edge(UP)
self.add(equation)
Group(equation, image).shift(1.5 * RIGHT)
question = TextMobject("What is\\\\this?")
question.scale(2.5)
question.to_edge(LEFT)
arrow = Arrow(
question.get_top(),
equation.get_left(),
buff=0.5,
path_arc=-90 * DEGREES,
)
arrow.set_stroke(width=5)
self.add(question, arrow)
class ShowNewton(Scene):
def construct(self):
pass
class ShowCupOfWater(Scene):
def construct(self):
pass

View file

@ -1,785 +0,0 @@
from manimlib.imports import *
class WriteHeatEquationTemplate(Scene):
CONFIG = {
"tex_mobject_config": {
"tex_to_color_map": {
"{T}": WHITE,
"{t}": YELLOW,
"{x}": GREEN,
"{y}": RED,
"{z}": BLUE,
"\\partial": WHITE,
"2": WHITE,
},
},
}
def get_d1_equation(self):
return TexMobject(
"{\\partial {T} \\over \\partial {t}}({x}, {t})", "=",
"\\alpha \\cdot",
"{\\partial^2 {T} \\over \\partial {x}^2} ({x}, {t})",
**self.tex_mobject_config
)
def get_d1_equation_without_inputs(self):
return TexMobject(
"{\\partial {T} \\over \\partial {t}}", "=",
"\\alpha \\cdot",
"{\\partial^2 {T} \\over \\partial {x}^2}",
**self.tex_mobject_config
)
def get_d3_equation(self):
return TexMobject(
"{\\partial {T} \\over \\partial {t}}", "=",
"\\alpha \\left(",
"{\\partial^2 {T} \\over \\partial {x}^2} + ",
"{\\partial^2 {T} \\over \\partial {y}^2} + ",
"{\\partial^2 {T} \\over \\partial {z}^2}",
"\\right)",
**self.tex_mobject_config
)
def get_general_equation(self):
return TexMobject(
"{\\partial {T} \\over \\partial {t}}", "=",
"\\alpha", "\\nabla^2 {T}",
**self.tex_mobject_config,
)
def get_d3_equation_with_inputs(self):
return TexMobject(
"{\\partial {T} \\over \\partial {t}}",
"({x}, {y}, {z}, {t})", "=",
"\\alpha \\left(",
"{\\partial^2 {T} \\over \\partial {x}^2}",
"({x}, {y}, {z}, {t}) + ",
"{\\partial^2 {T} \\over \\partial {y}^2}",
"({x}, {y}, {z}, {t}) + ",
"{\\partial^2 {T} \\over \\partial {z}^2}",
"({x}, {y}, {z}, {t})",
"\\right)",
**self.tex_mobject_config
)
def get_d1_words(self):
return TextMobject("Heat equation\\\\", "(1 dimension)")
def get_d3_words(self):
return TextMobject("Heat equation\\\\", "(3 dimensions)")
def get_d1_group(self):
group = VGroup(
self.get_d1_words(),
self.get_d1_equation(),
)
group.arrange(DOWN, buff=MED_LARGE_BUFF)
return group
def get_d3_group(self):
group = VGroup(
self.get_d3_words(),
self.get_d3_equation(),
)
group.arrange(DOWN, buff=MED_LARGE_BUFF)
return group
class HeatEquationIntroTitle(WriteHeatEquationTemplate):
def construct(self):
scale_factor = 1.25
title = TextMobject("The Heat Equation")
title.scale(scale_factor)
title.to_edge(UP)
equation = self.get_general_equation()
equation.scale(scale_factor)
equation.next_to(title, DOWN, MED_LARGE_BUFF)
equation.set_color_by_tex("{T}", RED)
self.play(
FadeInFrom(title, DOWN),
FadeInFrom(equation, UP),
)
self.wait()
class BringTogether(Scene):
def construct(self):
arrows = VGroup(Vector(2 * RIGHT), Vector(2 * LEFT))
arrows.arrange(RIGHT, buff=2)
words = TextMobject("Bring together")[0]
words.next_to(arrows, DOWN)
words.save_state()
words.space_out_submobjects(1.2)
self.play(
VFadeIn(words),
Restore(words),
arrows.arrange, RIGHT, {"buff": SMALL_BUFF},
VFadeIn(arrows),
)
self.play(FadeOut(words), FadeOut(arrows))
class FourierSeriesIntro(WriteHeatEquationTemplate):
def construct(self):
title_scale_value = 1.5
title = TextMobject(
"Fourier ", "Series",
)
title.scale(title_scale_value)
title.to_edge(UP)
title.generate_target()
details_coming = TextMobject("Details coming...")
details_coming.next_to(title.get_corner(DR), DOWN)
details_coming.set_color(LIGHT_GREY)
# physics = TextMobject("Physics")
heat = TextMobject("Heat")
heat.scale(title_scale_value)
physics = self.get_general_equation()
physics.set_color_by_tex("{T}", RED)
arrow1 = Arrow(LEFT, RIGHT)
arrow2 = Arrow(LEFT, RIGHT)
group = VGroup(
heat, arrow1, physics, arrow2, title.target
)
group.arrange(RIGHT)
# physics.align_to(title.target, UP)
group.to_edge(UP)
rot_square = Square()
rot_square.fade(1)
rot_square.add_updater(lambda m, dt: m.rotate(dt))
def update_heat_colors(heat):
colors = [YELLOW, RED]
vertices = rot_square.get_vertices()
letters = heat.family_members_with_points()
for letter, vertex in zip(letters, vertices):
alpha = (normalize(vertex)[0] + 1) / 2
i, sa = integer_interpolate(0, len(colors) - 1, alpha)
letter.set_color(interpolate_color(
colors[i], colors[i + 1], alpha,
))
heat.add_updater(update_heat_colors)
image = ImageMobject("Joseph Fourier")
image.set_height(5)
image.next_to(title, DOWN, LARGE_BUFF)
image.to_edge(LEFT)
name = TextMobject("Joseph", "Fourier")
name.next_to(image, DOWN)
bubble = ThoughtBubble(
height=2,
width=2.5,
direction=RIGHT,
)
bubble.set_fill(opacity=0)
bubble.set_stroke(WHITE)
bubble.set_stroke(BLACK, 5, background=True)
bubble.shift(heat.get_center() - bubble.get_bubble_center())
bubble[:-1].shift(LEFT + 0.2 * DOWN)
bubble[:-1].rotate(-20 * DEGREES)
for mob in bubble[:-1]:
mob.rotate(20 * DEGREES)
# self.play(FadeInFromDown(title))
self.add(title)
self.play(
FadeInFromDown(image),
TransformFromCopy(
title.get_part_by_tex("Fourier"),
name.get_part_by_tex("Fourier"),
path_arc=90 * DEGREES,
),
FadeIn(name.get_part_by_tex("Joseph")),
)
self.play(Write(details_coming, run_time=1))
self.play(LaggedStartMap(FadeOut, details_coming[0], run_time=1))
self.wait()
self.add(rot_square)
self.play(
FadeInFrom(physics, RIGHT),
GrowArrow(arrow2),
FadeInFrom(heat, RIGHT),
GrowArrow(arrow1),
MoveToTarget(title),
)
self.play(ShowCreation(bubble))
self.wait(10)
class CompareODEToPDE(Scene):
def construct(self):
pass
class TodaysTargetWrapper(Scene):
def construct(self):
pass
class TwoGraphTypeTitles(Scene):
def construct(self):
left_title = TextMobject(
"Represent time\\\\with actual time"
)
left_title.shift(FRAME_WIDTH * LEFT / 4)
right_title = TextMobject(
"Represent time\\\\with an axis"
)
right_title.shift(FRAME_WIDTH * RIGHT / 4)
titles = VGroup(left_title, right_title)
for title in titles:
title.scale(1.25)
title.to_edge(UP)
self.play(FadeInFromDown(right_title))
self.wait()
self.play(FadeInFromDown(left_title))
self.wait()
class ShowPartialDerivativeSymbols(Scene):
def construct(self):
t2c = {
"{x}": GREEN,
"{t}": YELLOW,
}
d_derivs, del_derivs = VGroup(*[
VGroup(*[
TexMobject(
"{" + sym, "T", "\\over", sym, var + "}",
"(", "{x}", ",", "{t}", ")",
).set_color_by_tex_to_color_map(t2c)
for var in ("{x}", "{t}")
])
for sym in ("d", "\\partial")
])
dTdx, dTdt = d_derivs
delTdelx, delTdelx = del_derivs
dels = VGroup(*it.chain(*[
del_deriv.get_parts_by_tex("\\partial")
for del_deriv in del_derivs
]))
dTdx.to_edge(UP)
self.play(FadeInFrom(dTdx, DOWN))
self.wait()
self.play(ShowCreationThenFadeAround(dTdx[3:5]))
self.play(ShowCreationThenFadeAround(dTdx[:2]))
self.wait()
dTdt.move_to(dTdx)
self.play(
dTdx.next_to, dTdt, RIGHT, {"buff": 1.5},
dTdx.set_opacity, 0.5,
FadeInFromDown(dTdt)
)
self.wait()
for m1, m2 in zip(d_derivs, del_derivs):
m2.move_to(m1)
pd_words = TextMobject("Partial derivatives")
pd_words.next_to(del_derivs, DOWN, MED_LARGE_BUFF)
self.play(
Write(pd_words),
dTdx.set_opacity, 1,
run_time=1,
)
self.wait()
self.play(
ReplacementTransform(d_derivs, del_derivs)
)
self.play(
LaggedStartMap(
ShowCreationThenFadeAround,
dels,
surrounding_rectangle_config={
"color": BLUE,
"buff": 0.5 * SMALL_BUFF,
"stroke_width": 2,
}
)
)
self.wait()
num_words = VGroup(*[
TextMobject(
"Change in $T$\\\\caused by {}",
"$\\partial$", "${}$".format(var),
arg_separator="",
).set_color_by_tex_to_color_map(t2c)
for var in ("{x}", "{t}")
])
num_words.scale(0.8)
for word, deriv in zip(num_words, del_derivs):
num = deriv[:2]
word.move_to(num, UP)
word.to_edge(UP, buff=MED_SMALL_BUFF)
deriv.rect = SurroundingRectangle(
num,
buff=SMALL_BUFF,
stroke_width=2,
color=word[-1].get_color(),
)
deriv.rect.mob = num
deriv.rect.add_updater(lambda r: r.move_to(r.mob))
self.play(
Write(num_words[1]),
VGroup(del_derivs, pd_words).shift, DOWN,
ShowCreation(del_derivs[1].rect),
)
self.play(
Write(num_words[0]),
ShowCreation(del_derivs[0].rect),
)
self.wait()
class WriteHeatEquation(WriteHeatEquationTemplate):
def construct(self):
title = TextMobject("The Heat Equation")
title.to_edge(UP)
equation = self.get_d1_equation()
equation.next_to(title, DOWN)
eq_i = equation.index_of_part_by_tex("=")
dt_part = equation[:eq_i]
dx_part = equation[eq_i + 3:]
dt_rect = SurroundingRectangle(dt_part)
dt_rect.set_stroke(YELLOW, 2)
dx_rect = SurroundingRectangle(dx_part)
dx_rect.set_stroke(GREEN, 2)
two_outlines = equation.get_parts_by_tex("2").copy()
two_outlines.set_stroke(YELLOW, 2)
two_outlines.set_fill(opacity=0)
to_be_explained = TextMobject(
"To be explained shortly..."
)
to_be_explained.scale(0.7)
to_be_explained.next_to(equation, RIGHT, MED_LARGE_BUFF)
to_be_explained.fade(1)
pde = TextMobject("Partial Differential Equation")
pde.move_to(title)
del_outlines = equation.get_parts_by_tex("\\partial").copy()
del_outlines.set_stroke(YELLOW, 2)
del_outlines.set_fill(opacity=0)
self.play(
FadeInFrom(title, 0.5 * DOWN),
FadeInFrom(equation, 0.5 * UP),
)
self.wait()
self.play(ShowCreation(dt_rect))
self.wait()
self.play(TransformFromCopy(dt_rect, dx_rect))
self.play(ShowCreationThenDestruction(two_outlines))
self.wait()
self.play(Write(to_be_explained, run_time=1))
self.wait(2)
self.play(
ShowCreationThenDestruction(
del_outlines,
lag_ratio=0.1,
)
)
self.play(
FadeOutAndShift(title, UP),
FadeInFrom(pde, DOWN),
FadeOut(dt_rect),
FadeOut(dx_rect),
)
self.wait()
class Show3DEquation(WriteHeatEquationTemplate):
def construct(self):
equation = self.get_d3_equation_with_inputs()
equation.set_width(FRAME_WIDTH - 1)
inputs = VGroup(*it.chain(*[
equation.get_parts_by_tex(s)
for s in ["{x}", "{y}", "{z}", "{t}"]
]))
inputs.sort()
equation.to_edge(UP)
self.add(equation)
self.play(LaggedStartMap(
ShowCreationThenFadeAround, inputs,
surrounding_rectangle_config={
"buff": 0.05,
"stroke_width": 2,
}
))
self.wait()
class Show1DAnd3DEquations(WriteHeatEquationTemplate):
def construct(self):
d1_group = self.get_d1_group()
d3_group = self.get_d3_group()
d1_words, d1_equation = d1_group
d3_words, d3_equation = d3_group
groups = VGroup(d1_group, d3_group)
for group in groups:
group.arrange(DOWN, buff=MED_LARGE_BUFF)
groups.arrange(RIGHT, buff=1.5)
groups.to_edge(UP)
d3_rhs = d3_equation[9:-2]
d3_brace = Brace(d3_rhs, DOWN)
nabla_words = TextMobject("Sometimes written as")
nabla_words.match_width(d3_brace)
nabla_words.next_to(d3_brace, DOWN)
nabla_exp = TexMobject(
"\\nabla^2 {T}",
**self.tex_mobject_config,
)
nabla_exp.next_to(nabla_words, DOWN)
# nabla_group = VGroup(nabla_words, nabla_exp)
d1_group.save_state()
d1_group.center().to_edge(UP)
self.play(
Write(d1_words),
FadeInFrom(d1_equation, UP),
run_time=1,
)
self.wait(2)
self.play(
Restore(d1_group),
FadeInFrom(d3_group, LEFT)
)
self.wait()
self.play(
GrowFromCenter(d3_brace),
Write(nabla_words),
TransformFromCopy(d3_rhs, nabla_exp),
run_time=1,
)
self.wait()
class D1EquationNoInputs(WriteHeatEquationTemplate):
def construct(self):
equation = self.get_d1_equation_without_inputs()
equation.to_edge(UP)
# i1 = equation.index_of_part_by_tex("\\partial")
# i2 = equation.index_of_part_by_tex("\\cdot")
# equation[i1:i1 + 2].set_color(RED)
# equation[i2 + 1:i2 + 6].set_color(RED)
equation.set_color_by_tex("{T}", RED)
self.add(equation)
class AltHeatRHS(Scene):
def construct(self):
formula = TexMobject(
"{\\alpha \\over 2}", "\\Big(",
"T({x} - 1, {t}) + T({x} + 1, {t})"
"\\Big)",
tex_to_color_map={
"{x}": GREEN,
"{t}": YELLOW,
}
)
self.add(formula)
class CompareInputsOfGeneralCaseTo1D(WriteHeatEquation):
def construct(self):
three_d_expr, one_d_expr = [
TexMobject(
"{T}(" + inputs + ", {t})",
**self.tex_mobject_config,
)
for inputs in ["{x}, {y}, {z}", "{x}"]
]
for expr in three_d_expr, one_d_expr:
expr.scale(2)
expr.to_edge(UP)
x, y, z = [
three_d_expr.get_part_by_tex(letter)
for letter in ["x", "y", "z"]
]
self.play(FadeInFromDown(three_d_expr))
self.play(LaggedStartMap(
ShowCreationThenFadeAround,
VGroup(x, y, z)
))
self.wait()
low = 3
high = -3
self.play(
ReplacementTransform(three_d_expr[:low], one_d_expr[:low]),
ReplacementTransform(three_d_expr[high:], one_d_expr[high:]),
three_d_expr[low:high].scale, 0,
)
self.wait()
class ShowLaplacian(WriteHeatEquation):
def construct(self):
equation = self.get_d3_equation()
equation.to_edge(UP, buff=MED_SMALL_BUFF)
parts = VGroup()
plusses = VGroup()
for char in "xyz":
index = equation.index_of_part_by_tex(
"{" + char + "}"
)
part = equation[index - 6:index + 3]
rect = SurroundingRectangle(part)
rect.match_color(equation[index])
parts.add(part)
part.rect = rect
if char in "yz":
plus = equation[index - 8]
part.plus = plus
plusses.add(plus)
lp = equation.get_part_by_tex("(")
rp = equation.get_part_by_tex(")")
for part in parts:
part.rp = rp.copy()
part.rp.next_to(part, RIGHT, SMALL_BUFF)
part.rp.align_to(lp, UP)
rp.become(parts[0].rp)
# Show new second derivatives
self.add(*equation)
self.remove(*plusses, *parts[1], *parts[2])
for part in parts[1:]:
self.play(
rp.become, part.rp,
FadeInFrom(part, LEFT),
Write(part.plus),
ShowCreation(part.rect),
)
self.play(
FadeOut(part.rect),
)
self.wait()
# Show laplacian
brace = Brace(parts, DOWN)
laplacian = TexMobject("\\nabla^2", "T")
laplacian.next_to(brace, DOWN)
laplacian_name = TextMobject(
"``Laplacian''"
)
laplacian_name.next_to(laplacian, DOWN)
T_parts = VGroup(*[part[3] for part in parts])
non_T_parts = VGroup(*[
VGroup(*part[:3], *part[4:])
for part in parts
])
self.play(GrowFromCenter(brace))
self.play(Write(laplacian_name))
self.play(
TransformFromCopy(non_T_parts, laplacian[0])
)
self.play(
TransformFromCopy(T_parts, laplacian[1])
)
self.wait(3)
class AskAboutActuallySolving(WriteHeatEquationTemplate):
def construct(self):
equation = self.get_d1_equation()
equation.center()
q1 = TextMobject("Solve for T?")
q1.next_to(equation, UP, LARGE_BUFF)
q2 = TextMobject("What does it \\emph{mean} to solve this?")
q2.next_to(equation, UP, LARGE_BUFF)
formula = TexMobject(
"T({x}, {t}) = \\sin\\big(a{x}\\big) e^{-\\alpha \\cdot a^2 {t}}",
tex_to_color_map={
"{x}": GREEN,
"{t}": YELLOW,
}
)
formula.next_to(equation, DOWN, LARGE_BUFF)
q3 = TextMobject("Is this it?")
arrow = Vector(LEFT, color=WHITE)
arrow.next_to(formula, RIGHT)
q3.next_to(arrow, RIGHT)
self.add(equation)
self.play(FadeInFromDown(q1))
self.wait()
self.play(
FadeInFromDown(q2),
q1.shift, 1.5 * UP,
)
self.play(FadeInFrom(formula, UP))
self.play(
GrowArrow(arrow),
FadeInFrom(q3, LEFT)
)
self.wait()
class PDEPatreonEndscreen(PatreonEndScreen):
CONFIG = {
"specific_patrons": [
"Juan Benet",
"Vassili Philippov",
"Burt Humburg",
"Matt Russell",
"Scott Gray",
"soekul",
"Tihan Seale",
"Richard Barthel",
"Ali Yahya",
"dave nicponski",
"Evan Phillips",
"Graham",
"Joseph Kelly",
"Kaustuv DeBiswas",
"LambdaLabs",
"Lukas Biewald",
"Mike Coleman",
"Peter Mcinerney",
"Quantopian",
"Roy Larson",
"Scott Walter, Ph.D.",
"Yana Chernobilsky",
"Yu Jun",
"Jordan Scales",
"D. Sivakumar",
"Lukas -krtek.net- Novy",
"John Shaughnessy",
"Britt Selvitelle",
"David Gow",
"J",
"Jonathan Wilson",
"Joseph John Cox",
"Magnus Dahlström",
"Randy C. Will",
"Ryan Atallah",
"Luc Ritchie",
"1stViewMaths",
"Adrian Robinson",
"Alexis Olson",
"Andreas Benjamin Brössel",
"Andrew Busey",
"Ankalagon",
"Antoine Bruguier",
"Antonio Juarez",
"Arjun Chakroborty",
"Art Ianuzzi",
"Awoo",
"Bernd Sing",
"Boris Veselinovich",
"Brian Staroselsky",
"Chad Hurst",
"Charles Southerland",
"Chris Connett",
"Christian Kaiser",
"Clark Gaebel",
"Cooper Jones",
"Danger Dai",
"Dave B",
"Dave Kester",
"David B. Hill",
"David Clark",
"DeathByShrimp",
"Delton Ding",
"eaglle",
"emptymachine",
"Eric Younge",
"Eryq Ouithaqueue",
"Federico Lebron",
"Giovanni Filippi",
"Hal Hildebrand",
"Hitoshi Yamauchi",
"Isaac Jeffrey Lee",
"j eduardo perez",
"Jacob Magnuson",
"Jameel Syed",
"Jason Hise",
"Jeff Linse",
"Jeff Straathof",
"John Griffith",
"John Haley",
"John V Wertheim",
"Jonathan Eppele",
"Kai-Siang Ang",
"Kanan Gill",
"L0j1k",
"Lee Beck",
"Lee Redden",
"Linh Tran",
"Ludwig Schubert",
"Magister Mugit",
"Mark B Bahu",
"Mark Heising",
"Martin Price",
"Mathias Jansson",
"Matt Langford",
"Matt Roveto",
"Matthew Bouchard",
"Matthew Cocke",
"Michael Faust",
"Michael Hardel",
"Mirik Gogri",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nero Li",
"Nikita Lesnikov",
"Omar Zrien",
"Owen Campbell-Moore",
"Peter Ehrnstrom",
"RedAgent14",
"rehmi post",
"Richard Burgmann",
"Richard Comish",
"Ripta Pasay",
"Rish Kundalia",
"Robert Teed",
"Roobie",
"Ryan Williams",
"Sachit Nagpal",
"Solara570",
"Stevie Metke",
"Tal Einav",
"Ted Suzman",
"Thomas Tarler",
"Tom Fleming",
"Valeriy Skobelev",
"Xavier Bernard",
"Yavor Ivanov",
"Yaw Etse",
"YinYangBalance.Asia",
"Zach Cardwell",
],
}

View file

@ -1,303 +0,0 @@
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,
}
}

View file

@ -1,175 +0,0 @@
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)

Some files were not shown because too many files have changed in this diff Show more