Merge remote-tracking branch 'upstream/master' into shaders

This commit is contained in:
GZTime 2021-04-09 20:09:43 +08:00
commit 1f6e911d60
469 changed files with 10334 additions and 298791 deletions

View file

@ -1,14 +0,0 @@
### If this is a support request:
**Please attempt to solve the problem on your own before opening an issue.**
Between old issues, StackOverflow, and Google, you should be able to find
solutions to most of the common problems.
Include at least:
1. Steps to reproduce the issue (e.g. the command you ran)
2. The unexpected behavior that occurred (e.g. error messages or screenshots)
3. The environment (e.g. operating system and version of manim)
### If this is a feature request:
Include the motivation for making this change.

20
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,20 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
### Describe the bug
<!-- A clear and concise description of what the bug is. -->
**Code**:
<!-- The code you run which reflect the bug. -->
**Wrong display or Error traceback**:
<!-- the wrong display result of the code you run, or the error Traceback -->
### Additional context
<!-- Add any other context about the problem here. -->

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Ask A Question
url: https://github.com/3b1b/manim/discussions/categories/q-a
about: Please ask questions you encountered here.

View file

@ -0,0 +1,23 @@
---
name: Error when using
about: The error you encountered while using manim
title: ''
labels: ''
assignees: ''
---
### Describe the error
<!-- A clear and concise description of what you want to make. -->
### Code and Error
**Code**:
<!-- The code you run -->
**Error**:
<!-- The error traceback you get when run your code -->
### Environment
**OS System**:
**manim version**: master <!-- make sure you are using the latest version of master branch -->
**python version**:

View file

@ -1,9 +1,18 @@
Thanks for contributing to manim!
<!-- Thanks for contributing to manim!
Please ensure that your pull request works with the latest version of manim.
-->
**Please ensure that your pull request works with the latest version of manim.**
You should also include:
## Motivation
<!-- Outline your motivation: In what way do your changes improve the library? -->
1. The motivation for making this change (or link the relevant issues)
2. How you tested the new behavior (e.g. a minimal working example, before/after
screenshots, gifs, commands, etc.) This is rather informal at the moment, but
the goal is to show us how you know the pull request works as intended.
## Proposed changes
<!-- What you changed in those files -->
-
-
-
## Test
<!-- How do you test your changes -->
**Code**:
**Result**:

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

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

30
.github/workflows/publish.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*

170
.gitignore vendored
View file

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

View file

@ -1,30 +0,0 @@
language: python
dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069)
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

171
README.md
View file

@ -1,155 +1,114 @@
![logo](logo/cropped.png)
<p align="center">
<a href="https://github.com/3b1b/manim">
<img src="https://raw.githubusercontent.com/3b1b/manim/master/logo/cropped.png">
</a>
</p>
[![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/learn/manim/)
[![pypi version](https://img.shields.io/pypi/v/manimgl?logo=pypi)](https://pypi.org/project/manimgl/)
[![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)
[![Manim Subreddit](https://img.shields.io/reddit/subreddit-subscribers/manim.svg?color=ff4301&label=reddit&logo=reddit)](https://www.reddit.com/r/manim/)
[![Manim Discord](https://img.shields.io/discord/581738731934056449.svg?label=discord&logo=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/).
Manim is an engine for precise programatic animations, designed for creating explanatory math videos.
Note, there are two versions of manim. This repository began as a personal project by the author of [3Blue1Brown](https://www.3blue1brown.com/) for the purpose of animating those videos, with video-specific code available [here](https://github.com/3b1b/videos). In 2020 a group of developers forked it into what is now the [community edition](https://github.com/ManimCommunity/manim/), with a goal of being more stable, better tested, quicker to respond to community contributions, and all around friendlier to get started with. You can engage with that community by joining the discord.
Since the fork, this version has evolved to work on top of OpenGL, and allows real-time rendering to an interactive window before scenes are finalized and written to a file.
## Installation
Manim runs on Python 3.7. You can install it from PyPI via pip:
Manim runs on Python 3.6 or higher (Python 3.8 is recommended).
System requirements are [FFmpeg](https://ffmpeg.org/), [OpenGL](https://www.opengl.org/) and [LaTeX](https://www.latex-project.org) (optional, if you want to use LaTeX).
For Linux, [Pango](https://pango.gnome.org) along with it's developerment headers are required. See instruction [here](https://github.com/ManimCommunity/ManimPango#building).
### Directly
```sh
pip3 install manimlib
```
# Install manimgl
pip install manimgl
System requirements are [cairo](https://www.cairographics.org), [ffmpeg](https://www.ffmpeg.org), [sox](http://sox.sourceforge.net), [latex](https://www.latex-project.org) (optional, if you want to use LaTeX).
You can now use it via the `manim` command. For example:
```sh
manim my_project.py MyScene
# Try it out
manimgl
```
For more options, take a look at the [Using manim](#using-manim) sections further below.
### Directly
If you want to hack on manimlib itself, clone this repository and in that directory execute:
```sh
# Install python requirements
python3 -m pip install -r requirements.txt
# Install manimgl
pip install -e .
# Try it out
python3 ./manim.py example_scenes.py SquareToCircle -pl
manimgl example_scenes.py OpeningManimExample
# or
manim-render example_scenes.py OpeningManimExample
```
### Directly (Windows)
1. [Install FFmpeg](https://www.wikihow.com/Install-FFmpeg-on-Windows).
2. [Install Cairo](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo). For most users, ``pycairo1.18.0cp37cp37mwin32.whl`` will do fine.
```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. Make sure that ``pycairo==1.17.1`` is changed to ``pycairo==1.18.0`` in requirements.txt.
2. Install a LaTeX distribution. [MiKTeX](https://miktex.org/download) is recommended.
3. Install the remaining Python packages.
```sh
git clone https://github.com/3b1b/manim.git
cd manim
pip3 install -r requirements.txt
python3 manim.py example_scenes.py SquareToCircle -pl
pip install -e .
manimgl example_scenes.py OpeningManimExample
```
### Mac OSX
1. Install FFmpeg, LaTeX in terminal using homebrew.
```sh
brew install ffmpeg mactex
```
2. Install latest version of manim using these command.
```sh
git clone https://github.com/3b1b/manim.git
cd manim
pip install -e .
manimgl example_scenes.py OpeningManimExample
```
## Anaconda Install
* Install sox and latex as above.
* Create a conda environment using `conda env create -f environment.yml`
* **WINDOWS ONLY** Install `pyreadline` via `pip install pyreadline`.
1. Install LaTeX as above.
2. Create a conda environment using `conda create -n manim python=3.8`.
3. Activate the environment using `conda activate manim`.
4. Install manimgl using `pip install -e .`.
### Using `virtualenv` and `virtualenvwrapper`
After installing `virtualenv` and `virtualenvwrapper`
```sh
git clone https://github.com/3b1b/manim.git
mkvirtualenv -a manim -r requirements.txt manim
python3 -m manim example_scenes.py SquareToCircle -pl
```
### Using Docker
Since it's a bit tricky to get all the dependencies set up just right, there is a Dockerfile and Compose file provided in this repo as well as [a premade image on Docker Hub](https://hub.docker.com/r/eulertour/manim/tags/). The Dockerfile contains instructions on how to build a manim image, while the Compose file contains instructions on how to run the image.
The 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
manimgl 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_config.yml for further configuration. To add your customization, you can either edit this file, or add another file by the same name "custom_config.yml" to whatever directory you are running manim from. For example [this is the one](https://github.com/3b1b/videos/blob/master/custom_config.yml) for 3blue1brown videos. There you can specify where videos should be output to, where manim should look for image files and sounds you want to read in, and other defaults regarding style and video quality.
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 the [example scenes](https://3b1b.github.io/manim/getting_started/example_scenes.html) to get a sense of how it is used, and feel free to look through the code behind [3blue1brown videos](https://github.com/3b1b/videos) for a much larger set of example. Note, however, that developments are often made to the library without considering backwards compatibility with those old videos. To run an old project with a guarantee that it will work, you will have to go back to the commit which completed that project.
### Documentation
Documentation is in progress at [eulertour.com/learn/manim](https://www.eulertour.com/learn/manim/).
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.
### Live Streaming
To live stream your animations, simply run manim with the `--livestream` option.
```sh
> python -m manim --livestream
Writing to media/videos/scene/scene/1080p30/LiveStreamTemp.mp4
Manim is now running in streaming mode. Stream animations by passing
them to manim.play(), e.g.
>>> c = Circle()
>>> manim.play(ShowCreation(c))
>>>
```
It is also possible to stream directly to Twitch. To do that simply pass
`--livestream` and `--to-twitch to manim` and specify the stream key with
`--with-key`. Then when you follow the above example the stream will directly
start on your Twitch channel (with no audio support).
[manim-kindergarten](https://github.com/manim-kindergarten/) wrote and collected some useful extra classes and some codes of videos in [manim_sandbox repo](https://github.com/manim-kindergarten/manim_sandbox).
## Contributing
Is always welcome. In particular, there is a dire need for tests and documentation.
Is always welcome. As mentioned above, the [community edition](https://github.com/ManimCommunity/manim) has the most active ecosystem for contributions, with testing and continuous integration, but pull requests are welcome here too. Please explain the motivation for a given change and examples of its effect.
## License
All files in the 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.

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.animate.stretch(4, dim=0))
# self.play(Rotate(circle, TAU / 4))
# self.play(circle.animate.shift(2 * RIGHT), circle.animate.scale(0.25))
# circle.insert_n_curves(10)
# self.play(circle.animate.apply_complex_function(lambda z: z**2))
class SquareToCircleEmbed(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
self.add(circle)
self.wait()
self.play(circle.animate.stretch(4, dim=0))
self.wait(1.5)
self.play(Rotate(circle, TAU / 4))
self.wait(1.5)
self.play(circle.animate.shift(2 * RIGHT), circle.animate.scale(0.25))
self.wait(1.5)
circle.insert_n_curves(10)
self.play(circle.animate.apply_complex_function(lambda z: z**2))
self.wait(2)

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.

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 multiple backend rendering. There is better documentation and
a more open contribution community.
About this documentation
------------------------
This documentation is based on the version in `3b1b/manim <https://github.com/3b1b/manim>`_.
Created by `TonyCrane <https://github.com/TonyCrane>`_ ("鹤翔万里" in Chinese) and in production.
Among them, the ``manim_example_ext`` extension for Sphinx refers to
`the documentation of ManimCommunity <https://docs.manim.community/>`_.
If you want to contribute to manim or this document, please see: :doc:`contributing`

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_config
==============
``directories``
---------------
- ``mirror_module_path``
(``True`` or ``False``) Whether to create a folder named the name of the
running file under the ``output`` path, and save the output (``images/``
or ``videos/``) in it.
- ``output``
Output file path, the videos will be saved in the ``videos/`` folder under it,
and the pictures will be saved in the ``images/`` folder under it.
For example, if you set ``output`` to ``"/.../manim/output"`` and
``mirror_module_path`` to ``False``, then you exported ``Scene1`` in the code
file and saved the last frame, then the final directory structure will be like:
.. code-block:: text
:emphasize-lines: 9, 11
manim/
├── manimlib/
│ ├── animation/
│ ├── ...
│ ├── default_config.yml
│ └── window.py
├── output/
│ ├── images
│ │ └── Scene1.png
│ └── videos
│ └── Scene1.mp4
├── code.py
└── custom_config.yml
But if you set ``mirror_module_path`` to ``True``, the directory structure will be:
.. code-block:: text
:emphasize-lines: 8
manim/
├── manimlib/
│ ├── animation/
│ ├── ...
│ ├── default_config.yml
│ └── window.py
├── output/
│ └── code/
│ ├── images
│ │ └── Scene1.png
│ └── videos
│ └── Scene1.mp4
├── code.py
└── custom_config.yml
- ``raster_images``
The directory for storing raster images to be used in the code (including
``.jpg``, ``.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,89 @@
CLI flags and configuration
===========================
Command Line Interface
----------------------
To run manim, you need to enter the directory at the same level as ``manimlib/``
and enter the command in the following format into terminal:
.. code-block:: sh
manimgl <code>.py <Scene> <flags>
# or
manim-render <code>.py <Scene> <flags>
- ``<code>.py`` : The python file you wrote. Needs to be at the same level as ``manimlib/``, otherwise you need to use an absolute path or a relative path.
- ``<Scene>`` : The scene you want to render here. If it is not written or written incorrectly, it will list all for you to choose. And if there is only one ``Scene`` in the file, this class will be rendered directly.
- ``<flags>`` : CLI flags.
Some useful flags
^^^^^^^^^^^^^^^^^
- ``-w`` to write the scene to a file.
- ``-o`` to write the scene to a file and open the result.
- ``-s`` to skip to the end and just show the final frame.
- ``-so`` will save the final frame to an image and show it.
- ``-n <number>`` to skip ahead to the ``n``\ th animation of a scene.
- ``-f`` to make the playback window fullscreen.
All supported flags
^^^^^^^^^^^^^^^^^^^
========================================================== ====== =================================================================================================================================================================================================
flag abbr function
========================================================== ====== =================================================================================================================================================================================================
``--help`` ``-h`` Show the help message and exit
``--write_file`` ``-w`` Render the scene as a movie file
``--skip_animations`` ``-s`` Skip to the last frame
``--low_quality`` ``-l`` Render at a low quality (for faster rendering)
``--medium_quality`` ``-m`` Render at a medium quality
``--hd`` Render at a 1080p quality
``--uhd`` Render at a 4k quality
``--full_screen`` ``-f`` Show window in full screen
``--save_pngs`` ``-g`` Save each frame as a png
``--save_as_gif`` ``-i`` Save the video as gif
``--transparent`` ``-t`` Render to a movie file with an alpha channel
``--quiet`` ``-q``
``--write_all`` ``-a`` Write all the scenes from a file
``--open`` ``-o`` Automatically open the saved file once its done
``--finder`` Show the output file in finder
``--config`` Guide for automatic configuration
``--file_name FILE_NAME`` Name for the movie or image file
``--start_at_animation_number START_AT_ANIMATION_NUMBER`` ``-n`` Start rendering not from the first animation, but from another, specified by its index. If you 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_config
--------------
In order to perform more configuration (about directories, etc.) and permanently
change the default value (you don't have to add flags to the command every time),
you can modify ``custom_config.yml``. The meaning of each option is in
page :doc:`../documentation/custom_config`.
You can also use different ``custom_config.yml`` for different directories, such as
following the directory structure:
.. code-block:: text
manim/
├── manimlib/
│ ├── animation/
│ ├── ...
│ ├── default_config.yml
│ └── window.py
├── project/
│ ├── code.py
│ └── custom_config.yml
└── custom_config.yml
When you enter the ``project/`` folder and run ``manimgl code.py <Scene>``,
it will overwrite ``manim/custom_config.yml`` with ``custom_config.yml``
in the ``project`` folder.

View file

@ -0,0 +1,719 @@
Example Scenes
==============
After understanding the previous knowledge, we can understand more scenes.
Many example scenes are given in ``example_scenes.py``, let's start with
the simplest and one by one.
InteractiveDevlopment
---------------------
.. manim-example:: InteractiveDevlopment
:media: ../_static/example_scenes/InteractiveDevlopment.mp4
from manimlib import *
class InteractiveDevlopment(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square = Square()
self.play(ShowCreation(square))
self.wait()
# This opens an iPython termnial where you can keep writing
# lines as if they were part of this construct method.
# In particular, 'square', 'circle' and 'self' will all be
# part of the local namespace in that terminal.
self.embed()
# Try copying and pasting some of the lines below into
# the interactive shell
self.play(ReplacementTransform(square, circle))
self.wait()
self.play(circle.animate.stretch(4, 0))
self.play(Rotate(circle, 90 * DEGREES))
self.play(circle.animate.shift(2 * RIGHT).scale(0.25))
text = Text("""
In general, using the interactive shell
is very helpful when developing new scenes
""")
self.play(Write(text))
# In the interactive shell, you can just type
# play, add, remove, clear, wait, save_state and restore,
# instead of self.play, self.add, self.remove, etc.
# To interact with the window, type touch(). You can then
# scroll in the window, or zoom by holding down 'z' while scrolling,
# and change camera perspective by holding down 'd' while moving
# the mouse. Press 'r' to reset to the standard camera position.
# Press 'q' to stop interacting with the window and go back to
# typing new commands into the shell.
# In principle you can customize a scene to be responsive to
# mouse and keyboard interactions
always(circle.move_to, self.mouse_point)
This scene is similar to what we wrote in :doc:`quickstart`.
And how to interact has been written in the comments.
No more explanation here.
AnimatingMethods
----------------
.. manim-example:: AnimatingMethods
:media: ../_static/example_scenes/AnimatingMethods.mp4
class AnimatingMethods(Scene):
def construct(self):
grid = Tex(r"\pi").get_grid(10, 10, height=4)
self.add(grid)
# You can animate the application of mobject methods with the
# ".animate" syntax:
self.play(grid.animate.shift(LEFT))
# Alternatively, you can use the older syntax by passing the
# method and then the arguments to the scene's "play" function:
self.play(grid.shift, LEFT)
# Both of those will interpolate between the mobject's initial
# state and whatever happens when you apply that method.
# For this example, calling grid.shift(LEFT) would shift the
# grid one unit to the left, but both of the previous calls to
# "self.play" animate that motion.
# The same applies for any method, including those setting colors.
self.play(grid.animate.set_color(YELLOW))
self.wait()
self.play(grid.animate.set_submobject_colors_by_gradient(BLUE, GREEN))
self.wait()
self.play(grid.animate.set_height(TAU - MED_SMALL_BUFF))
self.wait()
# The method Mobject.apply_complex_function lets you apply arbitrary
# complex functions, treating the points defining the mobject as
# complex numbers.
self.play(grid.animate.apply_complex_function(np.exp), run_time=5)
self.wait()
# Even more generally, you could apply Mobject.apply_function,
# which takes in functions form R^3 to R^3
self.play(
grid.animate.apply_function(
lambda p: [
p[0] + 0.5 * math.sin(p[1]),
p[1] + 0.5 * math.sin(p[0]),
p[2]
]
),
run_time=5,
)
self.wait()
The new usage in this scene is ``.get_grid()`` and ``self.play(mob.animate.method(args))``.
- ``.get_grid()`` method will return a new mobject containing multiple copies of this one arranged in a grid.
- ``self.play(mob.animate.method(args))`` animates the method, and the details are in the comments above.
TextExample
-----------
.. manim-example:: TextExample
:media: ../_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.animate.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.animate.set_width(2),
run_time=3
)
self.wait()
# In general, you can alway call Mobject.add_updater, and pass in
# a function that you want to be called on every frame. The function
# should take in either one argument, the mobject, or two arguments,
# the mobject and the amount of time since the last frame.
now = self.time
w0 = square.get_width()
square.add_updater(
lambda m: m.set_width(w0 * math.cos(self.time - now))
)
self.wait(4 * PI)
The new classes and usage in this scene are ``always_redraw()``, ``DecimalNumber``, ``.to_edge()``,
``.center()``, ``always()``, ``f_always()``, ``.set_y()`` and ``.add_updater()``.
- ``always_redraw()`` function create a new mobject every frame.
- ``DecimalNumber`` is a variable number, speed it up by breaking it into ``Text`` characters.
- ``.to_edge()`` means to place the object on the edge of the screen.
- ``.center()`` means to place the object in the center of the screen.
- ``always(f, x)`` means that a certain function (``f(x)``) is executed every frame.
- ``f_always(f, g)`` is similar to ``always``, executed ``f(g())`` every frame.
- ``.set_y()`` means to set the ordinate of the object on the screen.
- ``.add_updater()`` sets an update function for the object. For example: ``mob1.add_updater(lambda mob: mob.next_to(mob2))`` means ``mob1.next_to(mob2)`` is executed every frame.
CoordinateSystemExample
-----------------------
.. manim-example:: CoordinateSystemExample
:media: ../_static/example_scenes/CoordinateSystemExample.mp4
class CoordinateSystemExample(Scene):
def construct(self):
axes = Axes(
# x-axis ranges from -1 to 10, with a default step size of 1
x_range=(-1, 10),
# y-axis ranges from -2 to 10 with a step size of 0.5
y_range=(-2, 2, 0.5),
# The axes will be stretched so as to match the specified
# height and width
height=6,
width=10,
# Axes is made of two NumberLine mobjects. You can specify
# their configuration with axis_config
axis_config={
"stroke_color": GREY_A,
"stroke_width": 2,
},
# Alternatively, you can specify configuration for just one
# of them, like this.
y_axis_config={
"include_tip": False,
}
)
# Keyword arguments of add_coordinate_labels can be used to
# configure the DecimalNumber mobjects which it creates and
# adds to the axes
axes.add_coordinate_labels(
font_size=20,
num_decimal_places=1,
)
self.add(axes)
# Axes descends from the CoordinateSystem class, meaning
# you can call call axes.coords_to_point, abbreviated to
# axes.c2p, to associate a set of coordinates with a point,
# like so:
dot = Dot(color=RED)
dot.move_to(axes.c2p(0, 0))
self.play(FadeIn(dot, scale=0.5))
self.play(dot.animate.move_to(axes.c2p(3, 2)))
self.wait()
self.play(dot.animate.move_to(axes.c2p(5, 0.5)))
self.wait()
# Similarly, you can call axes.point_to_coords, or axes.p2c
# print(axes.p2c(dot.get_center()))
# We can draw lines from the axes to better mark the coordinates
# of a given point.
# Here, the always_redraw command means that on each new frame
# the lines will be redrawn
h_line = always_redraw(lambda: axes.get_h_line(dot.get_left()))
v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom()))
self.play(
ShowCreation(h_line),
ShowCreation(v_line),
)
self.play(dot.animate.move_to(axes.c2p(3, -2)))
self.wait()
self.play(dot.animate.move_to(axes.c2p(1, 1)))
self.wait()
# If we tie the dot to a particular set of coordinates, notice
# that as we move the axes around it respects the coordinate
# system defined by them.
f_always(dot.move_to, lambda: axes.c2p(1, 1))
self.play(
axes.animate.scale(0.75),
axes.animate.to_corner(UL),
run_time=2,
)
self.wait()
self.play(FadeOut(VGroup(axes, dot, h_line, v_line)))
# Other coordinate systems you can play around with include
# ThreeDAxes, NumberPlane, and ComplexPlane.
GraphExample
------------
.. manim-example:: GraphExample
:media: ../_static/example_scenes/GraphExample.mp4
class GraphExample(Scene):
def construct(self):
axes = Axes((-3, 10), (-1, 8))
axes.add_coordinate_labels()
self.play(Write(axes, lag_ratio=0.01, run_time=1))
# Axes.get_graph will return the graph of a function
sin_graph = axes.get_graph(
lambda x: 2 * math.sin(x),
color=BLUE,
)
# By default, it draws it so as to somewhat smoothly interpolate
# between sampled points (x, f(x)). If the graph is meant to have
# a corner, though, you can set use_smoothing to False
relu_graph = axes.get_graph(
lambda x: max(x, 0),
use_smoothing=False,
color=YELLOW,
)
# For discontinuous functions, you can specify the point of
# discontinuity so that it does not try to draw over the gap.
step_graph = axes.get_graph(
lambda x: 2.0 if x > 3 else 1.0,
discontinuities=[3],
color=GREEN,
)
# Axes.get_graph_label takes in either a string or a mobject.
# If it's a string, it treats it as a LaTeX expression. By default
# it places the label next to the graph near the right side, and
# has it match the color of the graph
sin_label = axes.get_graph_label(sin_graph, "\\sin(x)")
relu_label = axes.get_graph_label(relu_graph, Text("ReLU"))
step_label = axes.get_graph_label(step_graph, Text("Step"), x=4)
self.play(
ShowCreation(sin_graph),
FadeIn(sin_label, RIGHT),
)
self.wait(2)
self.play(
ReplacementTransform(sin_graph, relu_graph),
FadeTransform(sin_label, relu_label),
)
self.wait()
self.play(
ReplacementTransform(relu_graph, step_graph),
FadeTransform(relu_label, step_label),
)
self.wait()
parabola = axes.get_graph(lambda x: 0.25 * x**2)
parabola.set_stroke(BLUE)
self.play(
FadeOut(step_graph),
FadeOut(step_label),
ShowCreation(parabola)
)
self.wait()
# You can use axes.input_to_graph_point, abbreviated
# to axes.i2gp, to find a particular point on a graph
dot = Dot(color=RED)
dot.move_to(axes.i2gp(2, parabola))
self.play(FadeIn(dot, scale=0.5))
# A value tracker lets us animate a parameter, usually
# with the intent of having other mobjects update based
# on the parameter
x_tracker = ValueTracker(2)
f_always(
dot.move_to,
lambda: axes.i2gp(x_tracker.get_value(), parabola)
)
self.play(x_tracker.animate.set_value(4), run_time=3)
self.play(x_tracker.animate.set_value(-2), run_time=3)
self.wait()
SurfaceExample
--------------
.. manim-example:: SurfaceExample
:media: ../_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_config.yml file
# day_texture = "EarthTextureMap"
# night_texture = "NightEarthTextureMap"
day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
surfaces = [
TexturedSurface(surface, day_texture, night_texture)
for surface in [sphere, torus1, torus2]
]
for mob in surfaces:
mob.shift(IN)
mob.mesh = SurfaceMesh(mob)
mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
# Set perspective
frame = self.camera.frame
frame.set_euler_angles(
theta=-30 * DEGREES,
phi=70 * DEGREES,
)
surface = surfaces[0]
self.play(
FadeIn(surface),
ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3),
)
for mob in surfaces:
mob.add(mob.mesh)
surface.save_state()
self.play(Rotate(surface, PI / 2), run_time=2)
for mob in surfaces[1:]:
mob.rotate(PI / 2)
self.play(
Transform(surface, surfaces[1]),
run_time=3
)
self.play(
Transform(surface, surfaces[2]),
# Move camera frame during the transition
frame.animate.increment_phi(-10 * DEGREES),
frame.animate.increment_theta(-20 * DEGREES),
run_time=3
)
# Add ambient rotation
frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
# Play around with where the light is
light_text = Text("You can move around the light source")
light_text.move_to(surface_text)
light_text.fix_in_frame()
self.play(FadeTransform(surface_text, light_text))
light = self.camera.light_source
self.add(light)
light.save_state()
self.play(light.animate.move_to(3 * IN), run_time=5)
self.play(light.animate.shift(10 * OUT), run_time=5)
drag_text = Text("Try moving the mouse while pressing d or s")
drag_text.move_to(light_text)
drag_text.fix_in_frame()
self.play(FadeTransform(light_text, drag_text))
self.wait()
This scene shows an example of using a three-dimensional surface, and
the related usage has been briefly described in the notes.
- ``.fix_in_frame()`` makes the object not change with the view angle of the screen, and is always displayed at a fixed position on the screen.
OpeningManimExample
-------------------
.. manim-example:: OpeningManimExample
:media: ../_static/example_scenes/OpeningManimExample.mp4
class OpeningManimExample(Scene):
def construct(self):
intro_words = Text("""
The original motivation for manim was to
better illustrate mathematical functions
as transformations.
""")
intro_words.to_edge(UP)
self.play(Write(intro_words))
self.wait(2)
# Linear transform
grid = NumberPlane((-10, 10), (-5, 5))
matrix = [[1, 1], [0, 1]]
linear_transform_words = VGroup(
Text("This is what the matrix"),
IntegerMatrix(matrix, include_background_rectangle=True),
Text("looks like")
)
linear_transform_words.arrange(RIGHT)
linear_transform_words.to_edge(UP)
linear_transform_words.set_stroke(BLACK, 10, background=True)
self.play(
ShowCreation(grid),
FadeTransform(intro_words, linear_transform_words)
)
self.wait()
self.play(grid.animate.apply_matrix(matrix), run_time=3)
self.wait()
# Complex map
c_grid = ComplexPlane()
moving_c_grid = c_grid.copy()
moving_c_grid.prepare_for_nonlinear_transform()
c_grid.set_stroke(BLUE_E, 1)
c_grid.add_coordinate_labels(font_size=24)
complex_map_words = TexText("""
Or thinking of the plane as $\\mathds{C}$,\\\\
this is the map $z \\rightarrow z^2$
""")
complex_map_words.to_corner(UR)
complex_map_words.set_stroke(BLACK, 5, background=True)
self.play(
FadeOut(grid),
Write(c_grid, run_time=3),
FadeIn(moving_c_grid),
FadeTransform(linear_transform_words, complex_map_words),
)
self.wait()
self.play(
moving_c_grid.animate.apply_complex_function(lambda z: z**2),
run_time=6,
)
self.wait(2)
This scene is a comprehensive application of a two-dimensional scene.
After seeing these scenes, you have already understood part of the
usage of manim. For more examples, see `the video code of 3b1b <https://github.com/3b1b/videos>`_.

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,69 @@
Installation
============
Manim runs on Python 3.6 or higher (Python 3.8 is recommended).
System requirements are
- `FFmpeg <https://ffmpeg.org/>`__
- `OpenGL <https://www.opengl.org//>`__ (included in python package ``PyOpenGL``)
- `LaTeX <https://www.latex-project.org>`__ (optional, if you want to use LaTeX)
- `Pango <https://pango.org>`__ (only for Linux)
Directly
--------
.. code-block:: sh
# Install manimgl
pip install manimgl
# Try it out
manimgl
If you want to hack on manimlib itself, clone this repository and in
that directory execute:
.. code-block:: sh
# Install python requirements
pip install -e .
# Try it out
manimgl example_scenes.py OpeningManimExample
# or
manim-render example_scenes.py OpeningManimExample
If you run the above command and no error message appears,
then you have successfully installed all the environments required by manim.
Directly (Windows)
------------------
1. `Install
FFmpeg <https://www.wikihow.com/Install-FFmpeg-on-Windows>`__, and make sure that its path is in the PATH environment variable.
2. Install a LaTeX distribution.
`TeXLive-full <http://tug.org/texlive/>`__ is recommended.
3. Install the remaining Python packages.
.. code-block:: sh
git clone https://github.com/3b1b/manim.git
cd manim
pip install -e .
manimgl example_scenes.py OpeningManimExample
For Anaconda
------------
- Install FFmpeg and LaTeX as above.
- Create a conda environment using
.. code-block:: sh
git clone https://github.com/3b1b/manim.git
cd manim
conda create -n manim python=3.8
conda activate manim
pip install -e .

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/
│ ├── ...
│ ├── default_config.yml
│ └── window.py
├── custom_config.yml
└── start.py
And paste the following code (I will explain the function of each line in detail later):
.. code-block:: python
:linenos:
from manimlib import *
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
self.add(circle)
And run this command:
.. code-block:: sh
manimgl start.py SquareToCircle
A window will pop up on the screen. And then you can :
- scroll the middle mouse button to move the screen up and down
- hold down the :kbd:`z` on the keyboard while scrolling the middle mouse button to zoom the screen
- hold down the :kbd:`s` key on the keyboard and move the mouse to pan the screen
- hold down the :kbd:`d` key on the keyboard and move the mouse to change the three-dimensional perspective.
Finally, you can close the window and exit the program by pressing :kbd:`q`.
Run this command again:
.. code-block:: sh
manimgl start.py SquareToCircle -os
At this time, no window will pop up. When the program is finished, this rendered
image will be automatically opened (saved in the subdirectory ``images/`` of the same
level directory of ``start.py`` by default):
.. image:: ../_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 import *
This will import all the classes that may be used when using manim.
**Line 3**:
.. code-block:: python
class SquareToCircle(Scene):
Create a :class:`Scene` subclass ``SquareToCircle``, which will be
the scene you write and render.
**Line 4**:
.. code-block:: python
def construct(self):
Write the ``construct()`` method, the content of which will determine
how to create the mobjects in the screen and what operations need to be performed.
**Line 5**:
.. code-block:: python
circle = Circle()
Create a circle (an instance of the :class:`Circle` class), called ``circle``
**Line 6~7**:
.. code-block:: python
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
Set the circle style by calling the circle's method.
- The ``.set_fill()`` method sets the fill color of this circle to blue (``BLUE``, defined in :doc:`../documentation/constants`), and the fill transparency to 0.5.
- The ``.set_stroke()`` method sets the stroke color of this circle to dark blue (``BLUE_E``, defined in :doc:`../documentation/constants`), and the stroke width to 4.
**Line 9**:
.. code-block:: python
self.add(circle)
Add this circle to the screen through the ``.add()`` method of :class:`Scene`.
Add animations
--------------
Let's change some codes and add some animations to make videos instead of just pictures.
.. code-block:: python
:linenos:
from manimlib import *
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square = Square()
self.play(ShowCreation(square))
self.wait()
self.play(ReplacementTransform(square, circle))
self.wait()
Run this command this time:
.. code-block:: sh
manimgl start.py SquareToCircle
The pop-up window will play animations of drawing a square and transforming
it into a circle. If you want to save this video, run:
.. code-block:: sh
manimgl start.py SquareToCircle -o
This time there will be no pop-up window, but the video file (saved in the subdirectory
``videos/`` of the same level directory of ``start.py`` by default) will be automatically
opened after the operation is over:
.. raw:: html
<video class="manim-video" controls loop autoplay src="../_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 ``manimgl start.py SquareToCircle``.
After the previous animation is executed, the ipython terminal will be opened on
the command line. After that, you can continue to write code in it, and the statement
you entered will be executed immediately after pressing :kbd:`Enter`.
For example: input the following lines (without comment lines) into it respectively
(``self.play`` can be abbreviated as ``play`` in this mode):
.. code-block:: python
# Stretched 4 times in the vertical direction
play(circle.animate.stretch(4, dim=0}))
# Rotate the ellipse 90°
play(Rotate(circle, TAU / 4))
# Move 2 units to the right and shrink to 1/4 of the original
play(circle.animate.shift(2 * RIGHT), circle.animate.scale(0.25))
# Insert 10 curves into circle for non-linear transformation (no animation will play)
circle.insert_n_curves(10)
# Apply a complex transformation of f(z)=z^2 to all points on the circle
play(circle.animate.apply_complex_function(lambda z: z**2))
# Close the window and exit the program
exit()
You will get an animation similar to the following:
.. raw:: html
<video class="manim-video" controls loop autoplay src="../_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
manimgl
You succeeded!
--------------
After reading the above content, you already know how to use manim.
Below you can see some examples, in the :doc:`example_scenes` page.
But before that, you'd better have a look at the :doc:`configuration` of manim.

View file

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

View file

@ -0,0 +1,144 @@
What's new
==========
Usage changes of new version manim
----------------------------------
There are many changes in the new version of manim, and here are only the changes that
may have an impact at the code writing level.
Some of the changes here may not have any major impact on the use, and some changes
that affect the use are not mentioned below.
This document is for reference only, see the source code for details.
- ``Animation``
- Added ``Fade`` as the parent class of ``FadeIn`` and ``FadeOut``
- ``FadeIn`` and ``FadeOut`` can be passed in ``shift`` and ``scale`` parameters
- Deleted ``FadeInFrom, FadeInFromDown, FadeOutAndShift, FadeOutAndShiftDown, FadeInFromLarge``, these can be used ``FadeIn, FadeOut`` to achieve the same effect more easily
- Added ``FadeTransform`` to cross fade between two objects, and subclass ``FadeTransformPieces``
- Added ``CountInFrom(decimal_mob, source_number=0)`` to count ``decimal_mob`` from ``source_number`` to the current value
- ``Rotating`` can directly pass in ``angle`` and ``axis`` without writing keywords ``angle=, axis=``
- ``Rotate`` has become a subclass of ``Rotating``, and the distortion effect in ``Transform`` will not appear
- Removed ``MoveCar`` animation
- Added ``TransformMatchingShapes(mobject, target_mobject)`` and ``TransformMatchingTex(mobject, target_mobject)``
- ``Camera``
- Removed all camera classes except ``Camera`` (``MappingCamera``, ``MovingCamera``, ``MultiCamera``) and all functions in ``ThreeDCamera``
- Implemented ``CameraFrame`` (as a ``Mobject``)
- Can be called by ``self.camera.frame`` in ``Scene``
- All methods of ``Mobject`` can be used, such as ``.shift()``, ``.scale()``, etc.
- Call ``.to_default_state()`` to place in the default position
- Set the Euler angles of the camera by ``.set_euler_angles(theta, phi, gamma)``
- Set three single Euler angles by ``.set_theta(theta)``, ``.set_phi(phi)``, ``.set_gamma(gamma)``
- Use ``.increment_theta(dtheta)``, ``.increment_phi(dphi)``, ``.increment_gamma(gamma)`` to increase the three Euler angles by a certain value. Can be used to realize automatic rotation ``self.camera.frame.add_updater(lambda mob, dt: mob.increment_theta(0.1 * dt))``
- ``Camera`` adds a light source, which is a ``Point``, which can be called by ``self.camera.light_source`` in ``Scene`` to move and so on. The default position is ``(- 10, 10, 10)``
- Delete ``Container``
- ``Mobject``
- ``svg`` related
- Added ``Checkmark`` and ``Exmark``
- Some unnecessary classes have been removed from ``drawings.py``
- Removed ``Code`` and ``Paragraph`` (by mistake)
- ``TexMobject`` is renamed to ``Tex``, ``TextMobject`` is renamed to ``TexText``
- ``font_size`` has been added to ``Tex``, ``TexText`` and ``Text``
- ``Tex`` and ``TexText`` added ``isolate``, which is a list, which will be automatically split
- Mobject ``types``
- Added a new class ``Surface``, which is the parent class of ``ParametricSurface`` and ``TexturedSurface``.
- Added the group ``SGroup`` for ``Surface``
- Added ``TexturedSurface(uv_surface, image_file, dark_image_file=None)``, where ``uv_surface`` is a ``Surface``, ``image_file`` is the image to be posted, and ``dark_image_file`` is the image to be posted in the dark (default and ``image_file`` is the same)
- Deleted ``Mobject1D``, ``Mobject2D``, ``PointCloudDot``
- Added ``DotCloud`` (a ``PMobject``), which has been greatly optimized
- Removed ``AbstractImageMobject``, ``ImageMobjectFromCamera``
- Removed ``sheen`` from ``VMobject``
- ``Mobject``
- Added ``gloss`` and ``shadow``, which are the numbers between ``[0, 1]`` respectively. There are four methods of ``.get_gloss()``, ``.set_gloss(gloss)``, ``.get_shadow()``, ``.set_shadow(shadow)``
- Added ``.get_grid(n_rows, n_cols)`` to copy into grid
- Added ``.set_color_by_code(glsl_code)`` to use GLSL code to change the color
- Added ``.set_color_by_xyz_func(glsl_snippet, min_value=-5.0, max_value=5.0, colormap="viridis")`` to pass in GLSL expression in the form of ``x,y,z``, the return value should be a floating point number
- Coordinate system (including ``Axes``, ``ThreeDAxes``, ``NumberPlane``, ``ComplexPlane``)
- No longer use ``x_min``, ``x_max``, ``y_min``, ``y_max``, but use ``x_range``, ``y_range`` as a ``np.array()``, containing three numbers ``np.array([ Minimum, maximum, step size])``
- Added the abbreviation ``.i2gp(x, graph)`` of ``.input_to_graph_point(x, graph)``
- Added some functions of the original ``GraphScene``
- Added ``.get_v_line(point)``, ``.get_h_line(point)`` to return the line from ``point`` to the two coordinate axes, and specify the line type through the keyword argument of ``line_func`` (default ``DashedLine``)
- Added ``.get_graph_label(graph, label, x, direction, buff, color)`` to return the label added to the image
- Added ``.get_v_line_to_graph(x, graph)``, ``.get_h_line_to_graph(x, graph)`` to return the line from the point with the abscissa of ``x`` on the ``graph`` to the two- axis line
- Added ``.angle_of_tangent(x, graph, dx=EPSILON)``, returns the inclination angle of ``graph`` at ``x``
- Added ``.slope_of_tangent(x, graph, dx=EPSILON)``, returns the slope of tangent line of ``graph`` at ``x``
- Added ``.get_tangent_line(x, graph, length=5)`` to return the tangent line of ``graph`` at ``x``
- Added ``.get_riemann_rectangles(graph, x_range, dx, input_sample_type, ...)`` to return Riemann rectangles (a ``VGroup``)
- The attribute ``number_line_config`` of ``Axes`` is renamed to ``axis_config``
- ``Axes`` original ``.get_coordinate_labels(x_values, y_values)`` method was renamed to ``.add_coordinate_labels(x_values, y_values)`` (but it is not added to the screen)
- ``.add_coordinate_labels(numbers)`` of ``ComplexPlane`` will directly add the coordinates to the screen
- ``NumberLine``
- No longer use ``x_min``, ``x_max``, ``tick_frequency``, but use ``x_range``, which is an array containing three numbers ``[min, max, step]``
- The original ``label_direction`` attribute changed to the ``line_to_number_direction`` attribute
- Replace ``tip_width`` and ``tip_height`` with ``tip_config`` (dictionary) attributes
- The original ``exclude_zero_from_default`` attribute is modified to the ``numbers_to_exclude`` attribute (default is None)
- The original ``.add_tick_marks()`` method was changed to the ``.add_ticks()`` method
- Delete the ``.get_number_mobjects(*numbers)`` method, only use the ``.add_numbers(x_values=None, excluding=None)`` method
- Three-dimensional objects
- Added ``SurfaceMesh(uv_surface)``, pass in a ``Surface`` to generate its uv mesh
- ``ParametricSurface`` no longer uses ``u_min, u_max, v_min, v_max``, but instead uses ``u_range, v_range``, which is a tuple (``(min, max)``), and ``resolution`` can be set larger, dont worry Speed issue
- Added ``Torus``, controlled by ``r1, r2`` keyword parameters
- Added ``Cylinder``, controlled by ``height, radius`` keyword parameters
- Added ``Line3D`` (extremely thin cylinder), controlled by the ``width`` keyword parameter
- Added ``Disk3D``, controlled by ``radius`` keyword parameter
- Add ``Square3D``, controlled by ``side_length`` keyword parameter
- Improved ``Cube`` and ``Prism``, the usage remains unchanged
- Other objects
- ``ParametricFunction`` is renamed to ``ParametricCurve``. Instead of using ``t_min, t_max, step_size``, use ``t_range``, which is an array of three numbers (``[t_min, t_max, step_size]``). ``dt`` was renamed to ``epsilon``. Other usage remains unchanged
- All ``TipableVMobject`` can pass in ``tip_length`` to control the style of ``tip``
- ``Line`` adds ``.set_points_by_ends(start, end, buff=0, path_arc=0)`` method
- ``Line`` added ``.get_projection(point)`` to return the projection position of ``point`` on a straight line
- ``Arrow`` adds three attributes of ``thickness, tip_width_ratio, tip_angle``
- ``CubicBezier`` is changed to ``a0, h0, h1, a1``, that is, only a third-order Bezier curve is supported
- ``Square`` can be initialized directly by passing in ``side_length`` instead of using the keyword ``side_length=``
- ``always_redraw(func, *args, **kwargs)`` supports incoming parameters ``*args, **kwargs``
- The ``digit_to_digit_buff`` property of ``DecimalNumber`` has been renamed to ``digit_buff_per_font_unit``, and the ``.scale()`` method has been improved
- ``ValueTracker`` adds ``value_type`` attribute, the default is ``np.float64``
- ``Scene``
- Removed all functions of ``GraphScene`` (moved to ``once_useful_constructs``), ``MovingCameraScene``, ``ReconfigurableScene``, ``SceneFromVideo``, ``ZoomedScene``, and ``ThreeDScene``. Because these can basically be achieved by adjusting ``CameraFrame`` (``self.camera.frame``)
- Currently ``SampleSpaceScene`` and ``VectorScene`` have not been changed for the new version, so it is not recommended to use (only ``Scene`` is recommended)
- Fix the export of gif, just use the ``-i`` option directly
- Added the ``.interact()`` method, during which the mouse and keyboard can be used to continue the interaction, which will be executed by default after the scene ends
- Added ``.embed()`` method, open iPython terminal to enter interactive mode
- Added ``.save_state()`` method to save the current state of the scene
- Added ``.restore()`` method to restore the entire scene to the saved state
- ``utils``
- A series of functions related to second-order Bezier have been added to ``utils/bezier.py``
- Added a function to read color map from ``matplotlib`` in ``utils/color.py``
- Added a series of related functions for processing folders/custom styles/object families
- ``resize_array``, ``resize_preserving_order``, ``resize_with_interpolation`` three functions have been added to ``utils/iterables.py``
- The definition of ``smooth`` is updated in ``utils/rate_functions.py``
- ``clip(a, min_a, max_a)`` function has been added to ``utils/simple_functions.py``
- Some functions have been improved in ``utils/space_ops.py``, some functions for space calculation, and functions for processing triangulation have been added
- ``constants``
- Fixed the aspect ratio of the screen to 16:9
- Deleted the old gray series (``LIGHT_GREY``, ``GREY``, ``DARK_GREY``, ``DARKER_GREY``), added a new series of gray ``GREY_A`` ~ ``GREY_E``

View file

@ -1,30 +1,36 @@
.. 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
getting_started/whatsnew
.. toctree::
:maxdepth: 2
:caption: Documentation
Indices and tables
==================
documentation/constants
documentation/custom_config
* :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,19 +0,0 @@
name: manim
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
- pip:
- pyreadline

View file

@ -1,136 +1,674 @@
#!/usr/bin/env python
from manimlib.imports import *
from manimlib import *
import numpy as np
# To watch one of these scenes, run the following:
# python -m manim example_scenes.py SquareToCircle -pl
#
# Use the flat -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(
"\\sum_{n=1}^\\infty "
"\\frac{1}{n^2} = \\frac{\\pi^2}{6}"
intro_words = Text("""
The original motivation for manim was to
better illustrate mathematical functions
as transformations.
""")
intro_words.to_edge(UP)
self.play(Write(intro_words))
self.wait(2)
# Linear transform
grid = NumberPlane((-10, 10), (-5, 5))
matrix = [[1, 1], [0, 1]]
linear_transform_words = VGroup(
Text("This is what the matrix"),
IntegerMatrix(matrix, include_background_rectangle=True),
Text("looks like")
)
VGroup(title, basel).arrange(DOWN)
linear_transform_words.arrange(RIGHT)
linear_transform_words.to_edge(UP)
linear_transform_words.set_stroke(BLACK, 10, background=True)
self.play(
Write(title),
FadeInFrom(basel, UP),
ShowCreation(grid),
FadeTransform(intro_words, linear_transform_words)
)
self.wait()
transform_title = TextMobject("That was a transform")
transform_title.to_corner(UP + LEFT)
self.play(
Transform(title, transform_title),
LaggedStart(*map(FadeOutAndShiftDown, basel)),
)
self.play(grid.animate.apply_matrix(matrix), run_time=3)
self.wait()
grid = NumberPlane()
grid_title = TextMobject("This is a grid")
grid_title.scale(1.5)
grid_title.move_to(transform_title)
# Complex map
c_grid = ComplexPlane()
moving_c_grid = c_grid.copy()
moving_c_grid.prepare_for_nonlinear_transform()
c_grid.set_stroke(BLUE_E, 1)
c_grid.add_coordinate_labels(font_size=24)
complex_map_words = TexText("""
Or thinking of the plane as $\\mathds{C}$,\\\\
this is the map $z \\rightarrow z^2$
""")
complex_map_words.to_corner(UR)
complex_map_words.set_stroke(BLACK, 5, background=True)
self.add(grid, grid_title) # Make sure title is on top of grid
self.play(
FadeOut(title),
FadeInFromDown(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"
)
grid_transform_title.move_to(grid_title, UL)
grid.prepare_for_nonlinear_transform()
self.play(
grid.apply_function,
lambda p: p + np.array([
np.sin(p[1]),
np.sin(p[0]),
0,
]),
run_time=3,
FadeOut(grid),
Write(c_grid, run_time=3),
FadeIn(moving_c_grid),
FadeTransform(linear_transform_words, complex_map_words),
)
self.wait()
self.play(
Transform(grid_title, grid_transform_title)
moving_c_grid.animate.apply_complex_function(lambda z: z**2),
run_time=6,
)
self.wait()
self.wait(2)
class SquareToCircle(Scene):
class AnimatingMethods(Scene):
def construct(self):
circle = Circle()
square = Square()
square.flip(RIGHT)
square.rotate(-3 * TAU / 8)
circle.set_fill(PINK, opacity=0.5)
grid = Tex(r"\pi").get_grid(10, 10, height=4)
self.add(grid)
self.play(ShowCreation(square))
self.play(Transform(square, circle))
self.play(FadeOut(square))
# You can animate the application of mobject methods with the
# ".animate" syntax:
self.play(grid.animate.shift(LEFT))
# Alternatively, you can use the older syntax by passing the
# method and then the arguments to the scene's "play" function:
self.play(grid.shift, LEFT)
class WarpSquare(Scene):
def construct(self):
square = Square()
self.play(ApplyPointwiseFunction(
lambda point: complex_to_R3(np.exp(R3_to_complex(point))),
square
))
# Both of those will interpolate between the mobject's initial
# state and whatever happens when you apply that method.
# For this example, calling grid.shift(LEFT) would shift the
# grid one unit to the left, but both of the previous calls to
# "self.play" animate that motion.
# The same applies for any method, including those setting colors.
self.play(grid.animate.set_color(YELLOW))
self.wait()
self.play(grid.animate.set_submobject_colors_by_gradient(BLUE, GREEN))
self.wait()
self.play(grid.animate.set_height(TAU - MED_SMALL_BUFF))
self.wait()
# The method Mobject.apply_complex_function lets you apply arbitrary
# complex functions, treating the points defining the mobject as
# complex numbers.
self.play(grid.animate.apply_complex_function(np.exp), run_time=5)
self.wait()
# Even more generally, you could apply Mobject.apply_function,
# which takes in functions form R^3 to R^3
self.play(
grid.animate.apply_function(
lambda p: [
p[0] + 0.5 * math.sin(p[1]),
p[1] + 0.5 * math.sin(p[0]),
p[2]
]
),
run_time=5,
)
self.wait()
class WriteStuff(Scene):
class TextExample(Scene):
def construct(self):
example_text = TextMobject(
"This is a 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}
)
fonts.set_width(FRAME_WIDTH - 1)
slant = Text(
"And the same as slant and weight",
font="Consolas",
t2s={"slant": ITALIC},
t2w={"weight": BOLD},
t2c={"slant": ORANGE, "weight": RED}
)
VGroup(fonts, slant).arrange(DOWN, buff=0.8)
self.play(FadeOut(text), FadeOut(difference, shift=DOWN))
self.play(Write(fonts))
self.wait()
self.play(Write(slant))
self.wait()
class TexTransformExample(Scene):
def construct(self):
to_isolate = ["B", "C", "=", "(", ")"]
lines = VGroup(
# Passing in muliple arguments to Tex will result
# in the same expression as if those arguments had
# been joined together, except that the submobject
# heirarchy of the resulting mobject ensure that the
# Tex mobject has a subject corresponding to
# each of these strings. For example, the Tex mobject
# below will have 5 subjects, corresponding to the
# expressions [A^2, +, B^2, =, C^2]
Tex("A^2", "+", "B^2", "=", "C^2"),
# Likewise here
Tex("A^2", "=", "C^2", "-", "B^2"),
# Alternatively, you can pass in the keyword argument
# "isolate" with a list of strings that should be out as
# their own submobject. So the line below is equivalent
# to the commented out line below it.
Tex("A^2 = (C + B)(C - B)", isolate=["A^2", *to_isolate]),
# Tex("A^2", "=", "(", "C", "+", "B", ")", "(", "C", "-", "B", ")"),
Tex("A = \\sqrt{(C + B)(C - B)}", isolate=["A", *to_isolate])
)
lines.arrange(DOWN, buff=LARGE_BUFF)
for line in lines:
line.set_color_by_tex_to_color_map({
"A": BLUE,
"B": TEAL,
"C": GREEN,
})
play_kw = {"run_time": 2}
self.add(lines[0])
# The animation TransformMatchingTex will line up parts
# of the source and target which have matching tex strings.
# Here, giving it a little path_arc makes each part sort of
# rotate into their final positions, which feels appropriate
# for the idea of rearranging an equation
self.play(
TransformMatchingTex(
lines[0].copy(), lines[1],
path_arc=90 * DEGREES,
),
**play_kw
)
self.wait()
# Now, we could try this again on the next line...
self.play(
TransformMatchingTex(lines[1].copy(), lines[2]),
**play_kw
)
self.wait()
# ...and this looks nice enough, but since there's no tex
# in lines[2] which matches "C^2" or "B^2", those terms fade
# out to nothing while the C and B terms fade in from nothing.
# If, however, we want the C^2 to go to C, and B^2 to go to B,
# we can specify that with a key map.
self.play(FadeOut(lines[2]))
self.play(
TransformMatchingTex(
lines[1].copy(), lines[2],
key_map={
"C^2": "C",
"B^2": "B",
}
),
**play_kw
)
self.wait()
# And to finish off, a simple TransformMatchingShapes would work
# just fine. But perhaps we want that exponent on A^2 to transform into
# the square root symbol. At the moment, lines[2] treats the expression
# A^2 as a unit, so we might create a new version of the same line which
# separates out just the A. This way, when TransformMatchingTex lines up
# all matching parts, the only mismatch will be between the "^2" from
# new_line2 and the "\sqrt" from the final line. By passing in,
# transform_mismatches=True, it will transform this "^2" part into
# the "\sqrt" part.
new_line2 = Tex("A^2 = (C + B)(C - B)", isolate=["A", *to_isolate])
new_line2.replace(lines[2])
new_line2.match_style(lines[2])
self.play(
TransformMatchingTex(
new_line2, lines[3],
transform_mismatches=True,
),
**play_kw
)
self.wait(3)
self.play(FadeOut(lines, RIGHT))
# Alternatively, if you don't want to think about breaking up
# the tex strings deliberately, you can TransformMatchingShapes,
# which will try to line up all pieces of a source mobject with
# those of a target, regardless of the submobject hierarchy in
# each one, according to whether those pieces have the same
# shape (as best it can).
source = Text("the morse code", height=1)
target = Text("here come dots", height=1)
self.play(Write(source))
self.wait()
kw = {"run_time": 3, "path_arc": PI / 2}
self.play(TransformMatchingShapes(source, target, **kw))
self.wait()
self.play(TransformMatchingShapes(target, source, **kw))
self.wait()
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.animate.scale(2),
rate_func=there_and_back,
run_time=5,
run_time=2,
)
self.wait()
self.play(
square.animate.set_width(5, stretch=True),
run_time=3,
)
self.wait()
self.play(
square.animate.set_width(2),
run_time=3
)
self.wait()
# 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 CoordinateSystemExample(Scene):
def construct(self):
axes = Axes(
# x-axis ranges from -1 to 10, with a default step size of 1
x_range=(-1, 10),
# y-axis ranges from -2 to 10 with a step size of 0.5
y_range=(-2, 2, 0.5),
# The axes will be stretched so as to match the specified
# height and width
height=6,
width=10,
# Axes is made of two NumberLine mobjects. You can specify
# their configuration with axis_config
axis_config={
"stroke_color": GREY_A,
"stroke_width": 2,
},
# Alternatively, you can specify configuration for just one
# of them, like this.
y_axis_config={
"include_tip": False,
}
)
# Keyword arguments of add_coordinate_labels can be used to
# configure the DecimalNumber mobjects which it creates and
# adds to the axes
axes.add_coordinate_labels(
font_size=20,
num_decimal_places=1,
)
self.add(axes)
# Axes descends from the CoordinateSystem class, meaning
# you can call call axes.coords_to_point, abbreviated to
# axes.c2p, to associate a set of coordinates with a point,
# like so:
dot = Dot(color=RED)
dot.move_to(axes.c2p(0, 0))
self.play(FadeIn(dot, scale=0.5))
self.play(dot.animate.move_to(axes.c2p(3, 2)))
self.wait()
self.play(dot.animate.move_to(axes.c2p(5, 0.5)))
self.wait()
# Similarly, you can call axes.point_to_coords, or axes.p2c
# print(axes.p2c(dot.get_center()))
# We can draw lines from the axes to better mark the coordinates
# of a given point.
# Here, the always_redraw command means that on each new frame
# the lines will be redrawn
h_line = always_redraw(lambda: axes.get_h_line(dot.get_left()))
v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom()))
self.play(
ShowCreation(h_line),
ShowCreation(v_line),
)
self.play(dot.animate.move_to(axes.c2p(3, -2)))
self.wait()
self.play(dot.animate.move_to(axes.c2p(1, 1)))
self.wait()
# If we tie the dot to a particular set of coordinates, notice
# that as we move the axes around it respects the coordinate
# system defined by them.
f_always(dot.move_to, lambda: axes.c2p(1, 1))
self.play(
axes.animate.scale(0.75).to_corner(UL),
run_time=2,
)
self.wait()
self.play(FadeOut(VGroup(axes, dot, h_line, v_line)))
# Other coordinate systems you can play around with include
# ThreeDAxes, NumberPlane, and ComplexPlane.
class GraphExample(Scene):
def construct(self):
axes = Axes((-3, 10), (-1, 8))
axes.add_coordinate_labels()
self.play(Write(axes, lag_ratio=0.01, run_time=1))
# Axes.get_graph will return the graph of a function
sin_graph = axes.get_graph(
lambda x: 2 * math.sin(x),
color=BLUE,
)
# By default, it draws it so as to somewhat smoothly interpolate
# between sampled points (x, f(x)). If the graph is meant to have
# a corner, though, you can set use_smoothing to False
relu_graph = axes.get_graph(
lambda x: max(x, 0),
use_smoothing=False,
color=YELLOW,
)
# For discontinuous functions, you can specify the point of
# discontinuity so that it does not try to draw over the gap.
step_graph = axes.get_graph(
lambda x: 2.0 if x > 3 else 1.0,
discontinuities=[3],
color=GREEN,
)
# Axes.get_graph_label takes in either a string or a mobject.
# If it's a string, it treats it as a LaTeX expression. By default
# it places the label next to the graph near the right side, and
# has it match the color of the graph
sin_label = axes.get_graph_label(sin_graph, "\\sin(x)")
relu_label = axes.get_graph_label(relu_graph, Text("ReLU"))
step_label = axes.get_graph_label(step_graph, Text("Step"), x=4)
self.play(
ShowCreation(sin_graph),
FadeIn(sin_label, RIGHT),
)
self.wait(2)
self.play(
ReplacementTransform(sin_graph, relu_graph),
FadeTransform(sin_label, relu_label),
)
self.wait()
self.play(
ReplacementTransform(relu_graph, step_graph),
FadeTransform(relu_label, step_label),
)
self.wait()
parabola = axes.get_graph(lambda x: 0.25 * x**2)
parabola.set_stroke(BLUE)
self.play(
FadeOut(step_graph),
FadeOut(step_label),
ShowCreation(parabola)
)
self.wait()
# You can use axes.input_to_graph_point, abbreviated
# to axes.i2gp, to find a particular point on a graph
dot = Dot(color=RED)
dot.move_to(axes.i2gp(2, parabola))
self.play(FadeIn(dot, scale=0.5))
# A value tracker lets us animate a parameter, usually
# with the intent of having other mobjects update based
# on the parameter
x_tracker = ValueTracker(2)
f_always(
dot.move_to,
lambda: axes.i2gp(x_tracker.get_value(), parabola)
)
self.play(x_tracker.animate.set_value(4), run_time=3)
self.play(x_tracker.animate.set_value(-2), run_time=3)
self.wait()
class SurfaceExample(Scene):
CONFIG = {
"camera_class": ThreeDCamera,
}
def construct(self):
surface_text = Text("For 3d scenes, try using surfaces")
surface_text.fix_in_frame()
surface_text.to_edge(UP)
self.add(surface_text)
self.wait(0.1)
torus1 = Torus(r1=1, r2=1)
torus2 = Torus(r1=3, r2=1)
sphere = Sphere(radius=3, resolution=torus1.resolution)
# You can texture a surface with up to two images, which will
# be interpreted as the side towards the light, and away from
# the light. These can be either urls, or paths to a local file
# in whatever you've set as the image directory in
# the custom_config.yml file
# day_texture = "EarthTextureMap"
# night_texture = "NightEarthTextureMap"
day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
surfaces = [
TexturedSurface(surface, day_texture, night_texture)
for surface in [sphere, torus1, torus2]
]
for mob in surfaces:
mob.shift(IN)
mob.mesh = SurfaceMesh(mob)
mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
# Set perspective
frame = self.camera.frame
frame.set_euler_angles(
theta=-30 * DEGREES,
phi=70 * DEGREES,
)
surface = surfaces[0]
self.play(
FadeIn(surface),
ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3),
)
for mob in surfaces:
mob.add(mob.mesh)
surface.save_state()
self.play(Rotate(surface, PI / 2), run_time=2)
for mob in surfaces[1:]:
mob.rotate(PI / 2)
self.play(
Transform(surface, surfaces[1]),
run_time=3
)
self.play(
Transform(surface, surfaces[2]),
# Move camera frame during the transition
frame.animate.increment_phi(-10 * DEGREES),
frame.animate.increment_theta(-20 * DEGREES),
run_time=3
)
# Add ambient rotation
frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
# Play around with where the light is
light_text = Text("You can move around the light source")
light_text.move_to(surface_text)
light_text.fix_in_frame()
self.play(FadeTransform(surface_text, light_text))
light = self.camera.light_source
self.add(light)
light.save_state()
self.play(light.animate.move_to(3 * IN), run_time=5)
self.play(light.animate.shift(10 * OUT), run_time=5)
drag_text = Text("Try moving the mouse while pressing d or s")
drag_text.move_to(light_text)
drag_text.fix_in_frame()
self.play(FadeTransform(light_text, drag_text))
self.wait()
class InteractiveDevelopment(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square = Square()
self.play(ShowCreation(square))
self.wait()
# This opens an iPython termnial where you can keep writing
# lines as if they were part of this construct method.
# In particular, 'square', 'circle' and 'self' will all be
# part of the local namespace in that terminal.
self.embed()
# Try copying and pasting some of the lines below into
# the interactive shell
self.play(ReplacementTransform(square, circle))
self.wait()
self.play(circle.animate.stretch(4, 0))
self.play(Rotate(circle, 90 * DEGREES))
self.play(circle.animate.shift(2 * RIGHT).scale(0.25))
text = Text("""
In general, using the interactive shell
is very helpful when developing new scenes
""")
self.play(Write(text))
# In the interactive shell, you can just type
# play, add, remove, clear, wait, save_state and restore,
# instead of self.play, self.add, self.remove, etc.
# To interact with the window, type touch(). You can then
# scroll in the window, or zoom by holding down 'z' while scrolling,
# and change camera perspective by holding down 'd' while moving
# the mouse. Press 'r' to reset to the standard camera position.
# Press 'q' to stop interacting with the window and go back to
# typing new commands into the shell.
# In principle you can customize a scene to be responsive to
# mouse and keyboard interactions
always(circle.move_to, self.mouse_point)
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.wait(60)
# 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

File diff suppressed because it is too large Load diff

View file

@ -1,635 +0,0 @@
from manimlib.imports import *
import scipy.stats
CMARK_TEX = "\\text{\\ding{51}}"
XMARK_TEX = "\\text{\\ding{55}}"
COIN_COLOR_MAP = {
"H": BLUE_E,
"T": RED_E,
}
class Histogram(Group):
CONFIG = {
"height": 5,
"width": 10,
"y_max": 1,
"y_axis_numbers_to_show": range(20, 120, 20),
"y_axis_label_height": 0.25,
"y_tick_freq": 0.2,
"x_label_freq": 1,
"include_h_lines": True,
"h_line_style": {
"stroke_width": 1,
"stroke_color": LIGHT_GREY,
# "draw_stroke_behind_fill": True,
},
"bar_style": {
"stroke_width": 1,
"stroke_color": WHITE,
"fill_opacity": 1,
},
"bar_colors": [BLUE, GREEN]
}
def __init__(self, data, **kwargs):
super().__init__(**kwargs)
self.data = data
self.add_axes()
if self.include_h_lines:
self.add_h_lines()
self.add_bars(data)
self.add_x_axis_labels()
self.add_y_axis_labels()
def add_axes(self):
n_bars = len(self.data)
axes_config = {
"x_min": 0,
"x_max": n_bars,
"x_axis_config": {
"unit_size": self.width / n_bars,
"include_tip": False,
},
"y_min": 0,
"y_max": self.y_max,
"y_axis_config": {
"unit_size": self.height / self.y_max,
"include_tip": False,
"tick_frequency": self.y_tick_freq,
},
}
axes = Axes(**axes_config)
axes.center()
self.axes = axes
self.add(axes)
def add_h_lines(self):
axes = self.axes
axes.h_lines = VGroup()
for tick in axes.y_axis.tick_marks:
line = Line(**self.h_line_style)
line.match_width(axes.x_axis)
line.move_to(tick.get_center(), LEFT)
axes.h_lines.add(line)
axes.add(axes.h_lines)
def add_bars(self, data):
self.bars = self.get_bars(data)
self.add(self.bars)
def add_x_axis_labels(self):
axes = self.axes
axes.x_labels = VGroup()
for x, bar in list(enumerate(self.bars))[::self.x_label_freq]:
label = Integer(x)
label.set_height(0.25)
label.next_to(bar, DOWN)
axes.x_labels.add(label)
axes.add(axes.x_labels)
def add_y_axis_labels(self):
axes = self.axes
labels = VGroup()
for value in self.y_axis_numbers_to_show:
label = Integer(value, unit="\\%")
fix_percent(label[-1][0])
label.set_height(self.y_axis_label_height)
label.next_to(axes.y_axis.n2p(0.01 * value), LEFT)
labels.add(label)
axes.y_labels = labels
axes.y_axis.add(labels)
# Bar manipulations
def get_bars(self, data):
portions = np.array(data).astype(float)
total = portions.sum()
if total == 0:
portions[:] = 0
else:
portions /= total
bars = VGroup()
for x, prop in enumerate(portions):
bar = Rectangle()
width = get_norm(self.axes.c2p(1, 0) - self.axes.c2p(0, 0))
height = get_norm(self.axes.c2p(0, 1) - self.axes.c2p(0, 0))
bar.set_width(width)
bar.set_height(height * prop, stretch=True)
bar.move_to(self.axes.c2p(x, 0), DL)
bars.add(bar)
bars.set_submobject_colors_by_gradient(*self.bar_colors)
bars.set_style(**self.bar_style)
return bars
# Images of randomness
def fix_percent(sym):
# Really need to make this unneeded...
new_sym = sym.copy()
path_lengths = [len(path) for path in sym.get_subpaths()]
n = sum(path_lengths[:2])
p1 = sym.points[:n]
p2 = sym.points[n:]
sym.points = p1
new_sym.points = p2
sym.add(new_sym)
sym.lock_triangulation()
def get_random_process(choices, shuffle_time=2, total_time=3, change_rate=0.05,
h_buff=0.1, v_buff=0.1):
content = choices[0]
container = Square()
container.set_opacity(0)
container.set_width(content.get_width() + 2 * h_buff, stretch=True)
container.set_height(content.get_height() + 2 * v_buff, stretch=True)
container.move_to(content)
container.add(content)
container.time = 0
container.last_change_time = 0
def update(container, dt):
container.time += dt
t = container.time
change = all([
(t % total_time) < shuffle_time,
container.time - container.last_change_time > change_rate
])
if change:
mob = container.submobjects[0]
new_mob = random.choice(choices)
new_mob.match_height(mob)
new_mob.move_to(container, DL)
new_mob.shift(2 * np.random.random() * h_buff * RIGHT)
new_mob.shift(2 * np.random.random() * v_buff * UP)
container.set_submobjects([new_mob])
container.last_change_time = container.time
container.add_updater(update)
return container
def get_die_faces():
dot = Dot()
dot.set_width(0.15)
dot.set_color(BLUE_B)
square = Square()
square.round_corners(0.25)
square.set_stroke(WHITE, 2)
square.set_fill(DARKER_GREY, 1)
square.set_width(0.6)
edge_groups = [
(ORIGIN,),
(UL, DR),
(UL, ORIGIN, DR),
(UL, UR, DL, DR),
(UL, UR, ORIGIN, DL, DR),
(UL, UR, LEFT, RIGHT, DL, DR),
]
arrangements = VGroup(*[
VGroup(*[
dot.copy().move_to(square.get_bounding_box_point(ec))
for ec in edge_group
])
for edge_group in edge_groups
])
square.set_width(1)
faces = VGroup(*[
VGroup(square.copy(), arrangement)
for arrangement in arrangements
])
faces.arrange(RIGHT)
return faces
def get_random_die(**kwargs):
return get_random_process(get_die_faces(), **kwargs)
def get_random_card(height=1, **kwargs):
cards = DeckOfCards()
cards.set_height(height)
return get_random_process(cards, **kwargs)
# Coins
def get_coin(symbol, color=None):
if color is None:
color = COIN_COLOR_MAP.get(symbol, GREY_E)
coin = VGroup()
circ = Circle()
circ.set_fill(color, 1)
circ.set_stroke(WHITE, 1)
circ.set_height(1)
label = TextMobject(symbol)
label.set_height(0.5 * circ.get_height())
label.move_to(circ)
coin.add(circ, label)
coin.symbol = symbol
coin.lock_triangulation()
return coin
def get_random_coin(**kwargs):
return get_random_process([get_coin("H"), get_coin("T")], **kwargs)
def get_prob_coin_label(symbol="H", color=None, p=0.5, num_decimal_places=2):
label = TexMobject("P", "(", "00", ")", "=",)
coin = get_coin(symbol, color)
template = label.get_part_by_tex("00")
coin.replace(template)
label.replace_submobject(label.index_of_part(template), coin)
rhs = DecimalNumber(p, num_decimal_places=num_decimal_places)
rhs.next_to(label, RIGHT, buff=MED_SMALL_BUFF)
label.add(rhs)
return label
def get_q_box(mob):
box = SurroundingRectangle(mob)
box.set_stroke(WHITE, 1)
box.set_fill(GREY_E, 1)
q_marks = TexMobject("???")
max_width = 0.8 * box.get_width()
max_height = 0.8 * box.get_height()
if q_marks.get_width() > max_width:
q_marks.set_width(max_width)
if q_marks.get_height() > max_height:
q_marks.set_height(max_height)
q_marks.move_to(box)
box.add(q_marks)
return box
def get_coin_grid(bools, height=6):
coins = VGroup(*[
get_coin("H" if heads else "T")
for heads in bools
])
coins.arrange_in_grid()
coins.set_height(height)
return coins
def get_prob_positive_experience_label(include_equals=False,
include_decimal=False,
include_q_mark=False):
label = TexMobject(
"P", "(", "00000", ")",
)
pe = TextMobject("Positive\\\\experience")
pe.set_color(GREEN)
pe.replace(label[2], dim_to_match=0)
label.replace_submobject(2, pe)
VGroup(label[1], label[3]).match_height(
pe, stretch=True, about_edge=DOWN,
)
if include_equals:
eq = TexMobject("=").next_to(label, RIGHT)
label.add(eq)
if include_decimal:
decimal = DecimalNumber(0.95)
decimal.next_to(label, RIGHT)
decimal.set_color(YELLOW)
label.decimal = decimal
label.add(decimal)
if include_q_mark:
q_mark = TexMobject("?")
q_mark.relative_mob = label[-1]
q_mark.add_updater(
lambda m: m.next_to(m.relative_mob, RIGHT, SMALL_BUFF)
)
label.add(q_mark)
return label
def get_beta_dist_axes(y_max=20, y_unit=2, label_y=False, **kwargs):
config = {
"x_min": 0,
"x_max": 1,
"x_axis_config": {
"unit_size": 0.1,
"tick_frequency": 0.1,
"include_tip": False,
},
"y_min": 0,
"y_max": y_max,
"y_axis_config": {
"unit_size": 1,
"tick_frequency": y_unit,
"include_tip": False,
},
}
result = Axes(**config)
origin = result.c2p(0, 0)
kw = {
"about_point": origin,
"stretch": True,
}
result.x_axis.set_width(11, **kw)
result.y_axis.set_height(6, **kw)
x_vals = np.arange(0, 1, 0.2) + 0.2
result.x_axis.add_numbers(
*x_vals,
number_config={"num_decimal_places": 1}
)
if label_y:
result.y_axis.add_numbers(
*np.arange(y_unit, y_max, y_unit)
)
label = TextMobject("Probability density")
label.scale(0.5)
label.next_to(result.y_axis.get_top(), UR, SMALL_BUFF)
label.next_to(result.y_axis, UP, SMALL_BUFF)
label.align_to(result.y_axis.numbers, LEFT)
result.add(label)
result.y_axis_label = label
result.to_corner(DR, LARGE_BUFF)
return result
def scaled_pdf_axes(scale_factor=3.5):
axes = get_beta_dist_axes(
label_y=True,
y_unit=1,
)
axes.y_axis.numbers.set_submobjects([
*axes.y_axis.numbers[:5],
*axes.y_axis.numbers[4::5]
])
sf = scale_factor
axes.y_axis.stretch(sf, 1, about_point=axes.c2p(0, 0))
for number in axes.y_axis.numbers:
number.stretch(1 / sf, 1)
axes.y_axis_label.to_edge(LEFT)
axes.y_axis_label.add_background_rectangle(opacity=1)
axes.set_stroke(background=True)
return axes
def close_off_graph(axes, graph):
x_max = axes.x_axis.p2n(graph.get_end())
graph.add_line_to(axes.c2p(x_max, 0))
graph.add_line_to(axes.c2p(0, 0))
graph.lock_triangulation()
return graph
def get_beta_graph(axes, n_plus, n_minus, **kwargs):
dist = scipy.stats.beta(n_plus + 1, n_minus + 1)
graph = axes.get_graph(dist.pdf, **kwargs)
close_off_graph(axes, graph)
graph.set_stroke(BLUE, 2)
graph.set_fill(BLUE_E, 1)
graph.lock_triangulation()
return graph
def get_beta_label(n_plus, n_minus, point=ORIGIN):
template = TextMobject("Beta(", "00", ",", "00", ")")
template.scale(1.5)
a_label = Integer(n_plus + 1)
a_label.set_color(GREEN)
b_label = Integer(n_minus + 1)
b_label.set_color(RED)
for i, label in (1, a_label), (3, b_label):
label.match_height(template[i])
label.move_to(template[i], DOWN)
template.replace_submobject(i, label)
template.save_state()
template.arrange(RIGHT, buff=0.15)
for t1, t2 in zip(template, template.saved_state):
t1.align_to(t2, DOWN)
return template
def get_plusses_and_minuses(n_rows=15, n_cols=20, p=0.95):
result = VGroup()
for x in range(n_rows * n_cols):
if random.random() < p:
mob = TexMobject(CMARK_TEX)
mob.set_color(GREEN)
mob.is_plus = True
else:
mob = TexMobject(XMARK_TEX)
mob.set_color(RED)
mob.is_plus = False
mob.set_width(1)
result.add(mob)
result.arrange_in_grid(n_rows, n_cols)
result.set_width(5.5)
return result
def get_checks_and_crosses(bools, width=12):
result = VGroup()
for positive in bools:
if positive:
mob = TexMobject(CMARK_TEX)
mob.set_color(GREEN)
else:
mob = TexMobject(XMARK_TEX)
mob.set_color(RED)
mob.positive = positive
mob.set_width(0.5)
result.add(mob)
result.arrange(RIGHT, buff=MED_SMALL_BUFF)
result.set_width(width)
return result
def get_underlines(marks):
underlines = VGroup()
for mark in marks:
underlines.add(Underline(mark))
for line in underlines:
line.align_to(underlines[-1], DOWN)
return underlines
def get_random_checks_and_crosses(n=50, s=0.95, width=12):
return get_checks_and_crosses(
bools=(np.random.random(n) < s),
width=width
)
def get_random_num_row(s, n=10):
values = np.random.random(n)
nums = VGroup()
syms = VGroup()
for x, value in enumerate(values):
num = DecimalNumber(value)
num.set_height(0.25)
num.move_to(x * RIGHT)
num.positive = (num.get_value() < s)
if num.positive:
num.set_color(GREEN)
sym = TexMobject(CMARK_TEX)
else:
num.set_color(RED)
sym = TexMobject(XMARK_TEX)
sym.match_color(num)
sym.match_height(num)
sym.positive = num.positive
sym.next_to(num, UP)
nums.add(num)
syms.add(sym)
row = VGroup(nums, syms)
row.nums = nums
row.syms = syms
row.n_positive = sum([m.positive for m in nums])
row.set_width(10)
row.center().to_edge(UP)
return row
def get_prob_review_label(n_positive, n_negative, s=0.95):
label = TexMobject(
"P(",
f"{n_positive}\\,{CMARK_TEX}", ",\\,",
f"{n_negative}\\,{XMARK_TEX}",
"\\,|\\,",
"s = {:.2f}".format(s),
")",
)
label.set_color_by_tex_to_color_map({
CMARK_TEX: GREEN,
XMARK_TEX: RED,
"0.95": YELLOW,
})
return label
def get_binomial_formula(n, k, p):
n_mob = Integer(n, color=WHITE)
k_mob = Integer(k, color=GREEN)
nmk_mob = Integer(n - k, color=RED)
p_mob = DecimalNumber(p, color=YELLOW)
n_str = "N" * len(n_mob)
k_str = "K" * len(k_mob)
p_str = "P" * len(k_mob)
nmk_str = "M" * len(nmk_mob)
formula = TexMobject(
"\\left(",
"{" + n_str,
"\\over",
k_str + "}",
"\\right)",
"(", p_str, ")",
"^{" + k_str + "}",
"(1 - ", p_str, ")",
"^{" + nmk_str + "}",
)
parens = VGroup(formula[0], formula[4])
parens.space_out_submobjects(0.7)
formula.remove(formula.get_part_by_tex("\\over"))
pairs = (
(n_mob, n_str),
(k_mob, k_str),
(nmk_mob, nmk_str),
(p_mob, p_str),
)
for mob, tex in pairs:
parts = formula.get_parts_by_tex(tex)
for part in parts:
mob_copy = mob.copy()
i = formula.index_of_part_by_tex(tex)
mob_copy.match_height(part)
mob_copy.move_to(part, DOWN)
formula.replace_submobject(i, mob_copy)
terms = VGroup(
formula[:4],
formula[4:7],
formula[7],
formula[8:11],
formula[11],
)
ys = [term.get_y() for term in terms]
terms.arrange(RIGHT, buff=SMALL_BUFF)
terms[0].shift(SMALL_BUFF * LEFT)
for term, y in zip(terms, ys):
term.set_y(y)
return formula
def get_check_count_label(nc, nx, include_rect=True):
result = VGroup(
Integer(nc),
TexMobject(CMARK_TEX, color=GREEN),
Integer(nx),
TexMobject(XMARK_TEX, color=RED),
)
result.arrange(RIGHT, buff=SMALL_BUFF)
result[2:].shift(SMALL_BUFF * RIGHT)
if include_rect:
rect = SurroundingRectangle(result)
rect.set_stroke(WHITE, 1)
rect.set_fill(GREY_E, 1)
result.add_to_back(rect)
return result
def reverse_smooth(t):
return smooth(1 - t)
def get_region_under_curve(axes, graph, min_x, max_x):
props = [
binary_search(
function=lambda a: axes.x_axis.p2n(graph.pfp(a)),
target=x,
lower_bound=axes.x_min,
upper_bound=axes.x_max,
)
for x in [min_x, max_x]
]
region = graph.copy()
region.pointwise_become_partial(graph, *props)
region.add_line_to(axes.c2p(max_x, 0))
region.add_line_to(axes.c2p(min_x, 0))
region.add_line_to(region.get_start())
region.set_stroke(GREEN, 2)
region.set_fill(GREEN, 0.5)
region.axes = axes
region.graph = graph
region.min_x = min_x
region.max_x = max_x
return region

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,754 +0,0 @@
from manimlib.imports import *
from from_3b1b.active.sir import *
class LastFewMonths(Scene):
def construct(self):
words = TextMobject("Last ", "few\\\\", "months:")
words.set_height(4)
underlines = VGroup()
for word in words:
underline = Line(LEFT, RIGHT)
underline.match_width(word)
underline.next_to(word, DOWN, SMALL_BUFF)
underlines.add(underline)
underlines[0].stretch(1.4, 0, about_edge=LEFT)
underlines.set_color(BLUE)
# self.play(ShowCreation(underlines))
self.play(ShowIncreasingSubsets(words, run_time=0.75, rate_func=linear))
self.wait()
class UnemploymentTitle(Scene):
def construct(self):
words = TextMobject("Unemployment claims\\\\per week in the US")[0]
words.set_width(FRAME_WIDTH - 1)
words.to_edge(UP)
arrow = Arrow(
words.get_bottom(),
words.get_bottom() + 3 * RIGHT + 3 * DOWN,
stroke_width=10,
tip_length=0.5,
)
arrow.set_color(BLUE_E)
words.set_color(BLACK)
self.play(
ShowIncreasingSubsets(words),
ShowCreation(arrow),
)
self.wait()
class ExplainTracing(Scene):
def construct(self):
# Words
words = VGroup(
TextMobject("Testing, ", "Testing, ", "Testing!"),
TextMobject("Contact Tracing"),
)
words[0].set_color(GREEN)
words[1].set_color(BLUE_B)
words.set_width(FRAME_WIDTH - 2)
words.arrange(DOWN, buff=1)
self.play(ShowIncreasingSubsets(words[0], rate_func=linear))
self.wait()
self.play(Write(words[1], run_time=1))
self.wait()
self.play(
words[1].to_edge, UP,
FadeOutAndShift(words[0], 6 * UP)
)
ct_word = words[1][0]
# Groups
clusters = VGroup()
for x in range(4):
cluster = VGroup()
for y in range(4):
cluster.add(Randolph())
cluster.arrange_in_grid(buff=LARGE_BUFF)
clusters.add(cluster)
clusters.scale(0.5)
clusters.arrange_in_grid(buff=2)
clusters.set_height(4)
self.play(FadeIn(clusters))
pis = VGroup()
boxes = VGroup()
for cluster in clusters:
for pi in cluster:
pis.add(pi)
box = SurroundingRectangle(pi, buff=0.05)
boxes.add(box)
pi.box = box
boxes.set_stroke(WHITE, 1)
sicky = clusters[0][2]
covid_words = TextMobject("COVID-19\\\\Positive!")
covid_words.set_color(RED)
arrow = Vector(RIGHT, color=RED)
arrow.next_to(sicky, LEFT)
covid_words.next_to(arrow, LEFT, SMALL_BUFF)
self.play(
sicky.change, "sick",
sicky.set_color, "#9BBD37",
FadeInFrom(covid_words, RIGHT),
GrowArrow(arrow),
)
self.play(ShowCreation(sicky.box))
self.wait(2)
anims = []
for pi in clusters[0]:
if pi is not sicky:
anims.append(ApplyMethod(pi.change, "tired"))
anims.append(ShowCreation(pi.box))
self.play(*anims)
self.wait()
self.play(VFadeIn(
boxes[4:],
run_time=2,
rate_func=there_and_back_with_pause,
))
self.wait()
self.play(FadeOut(
VGroup(
covid_words,
arrow,
*boxes[:4],
*pis,
),
lag_ratio=0.1,
run_time=3,
))
self.play(ct_word.move_to, 2 * UP)
# Underlines
implies = TexMobject("\\Downarrow")
implies.scale(2)
implies.next_to(ct_word, DOWN, MED_LARGE_BUFF)
loc_tracking = TextMobject("Location Tracking")
loc_tracking.set_color(GREY_BROWN)
loc_tracking.match_height(ct_word)
loc_tracking.next_to(implies, DOWN, MED_LARGE_BUFF)
q_marks = TexMobject("???")
q_marks.scale(2)
q_marks.next_to(implies, RIGHT)
cross = Cross(implies)
cross.set_stroke(RED, 7)
self.play(
Write(implies),
FadeInFrom(loc_tracking, UP)
)
self.play(FadeIn(q_marks, lag_ratio=0.1))
self.wait()
parts = VGroup(ct_word[:7], ct_word[7:])
lines = VGroup()
for part in parts:
line = Line(part.get_left(), part.get_right())
line.align_to(part[0], DOWN)
line.shift(0.1 * DOWN)
lines.add(line)
ct_word.set_stroke(BLACK, 2, background=True)
self.add(lines[1], ct_word)
self.play(ShowCreation(lines[1]))
self.wait()
self.play(ShowCreation(lines[0]))
self.wait()
self.play(
ShowCreation(cross),
FadeOutAndShift(q_marks, RIGHT),
FadeOut(lines),
)
self.wait()
dp_3t = TextMobject("DP-3T")
dp_3t.match_height(ct_word)
dp_3t.move_to(loc_tracking)
dp_3t_long = TextMobject("Decentralized Privacy-Preserving Proximity Tracing")
dp_3t_long.next_to(dp_3t, DOWN, LARGE_BUFF)
arrow = Vector(UP)
arrow.set_stroke(width=8)
arrow.move_to(implies)
self.play(
FadeInFromDown(dp_3t),
FadeOut(loc_tracking),
FadeOut(implies),
FadeOut(cross),
ShowCreation(arrow)
)
self.play(Write(dp_3t_long))
self.wait()
class ContactTracingMisnomer(Scene):
def construct(self):
# Word play
words = TextMobject("Contact ", "Tracing")
words.scale(2)
rects = VGroup(*[
SurroundingRectangle(word, buff=0.2)
for word in words
])
expl1 = TextMobject("Doesn't ``trace'' you...")
expl2 = TextMobject("...or your contacts")
expls = VGroup(expl1, expl2)
colors = [RED, BLUE]
self.add(words)
for vect, rect, expl, color in zip([UP, DOWN], reversed(rects), expls, colors):
arrow = Vector(-vect)
arrow.next_to(rect, vect, SMALL_BUFF)
expl.next_to(arrow, vect, SMALL_BUFF)
rect.set_color(color)
arrow.set_color(color)
expl.set_color(color)
self.play(
FadeInFrom(expl, -vect),
GrowArrow(arrow),
ShowCreation(rect),
)
self.wait()
self.play(Write(
VGroup(*self.mobjects),
rate_func=lambda t: smooth(1 - t),
run_time=3,
))
class ContactTracingWords(Scene):
def construct(self):
words = TextMobject("Contact\\\\", "Tracing")
words.set_height(4)
for word in words:
self.add(word)
self.wait()
self.wait()
return
self.play(ShowIncreasingSubsets(words))
self.wait()
self.play(
words.set_height, 1,
words.to_corner, UL,
)
self.wait()
class WanderingDotsWithLines(Scene):
def construct(self):
sim = SIRSimulation(
city_population=20,
person_type=DotPerson,
person_config={
"color_map": {
"S": GREY,
"I": GREY,
"R": GREY,
},
"infection_ring_style": {
"stroke_color": YELLOW,
},
"max_speed": 0.5,
},
infection_time=100,
)
for person in sim.people:
person.set_status("S")
person.infection_start_time += random.random()
lines = VGroup()
max_dist = 1.25
def update_lines(lines):
lines.remove(*lines.submobjects)
for p1 in sim.people:
for p2 in sim.people:
if p1 is p2:
continue
dist = get_norm(p1.get_center() - p2.get_center())
if dist < max_dist:
line = Line(p1.get_center(), p2.get_center())
alpha = (max_dist - dist) / max_dist
line.set_stroke(
interpolate_color(WHITE, RED, alpha),
width=4 * alpha
)
lines.add(line)
lines.add_updater(update_lines)
self.add(lines)
self.add(sim)
self.wait(10)
for person in sim.people:
person.set_status("I")
person.infection_start_time += random.random()
self.wait(50)
class WhatAboutPeopleWithoutPhones(TeacherStudentsScene):
def construct(self):
self.student_says(
"What about people\\\\without phones?",
target_mode="sassy",
added_anims=[self.teacher.change, "guilty"]
)
self.change_student_modes("angry", "angry", "sassy")
self.wait()
self.play(self.teacher.change, "tease")
self.wait()
words = VectorizedPoint()
words.scale(1.5)
words.to_corner(UL)
self.play(
FadeInFromDown(words),
RemovePiCreatureBubble(self.students[2]),
*[
ApplyMethod(pi.change, "pondering", words)
for pi in self.pi_creatures
]
)
self.wait(5)
class PiGesture1(Scene):
def construct(self):
randy = Randolph(mode="raise_right_hand", height=2)
bubble = randy.get_bubble(
bubble_class=SpeechBubble,
height=2, width=3,
)
bubble.write("This one's\\\\great")
bubble.content.scale(0.8)
bubble.content.set_color(BLACK)
bubble.set_color(BLACK)
bubble.set_fill(opacity=0)
randy.set_stroke(BLACK, 5, background=True)
self.add(randy, bubble, bubble.content)
class PiGesture2(Scene):
def construct(self):
randy = Randolph(mode="raise_left_hand", height=2)
randy.look(UL)
# randy.flip()
randy.set_color(GREY_BROWN)
bubble = randy.get_bubble(
bubble_class=SpeechBubble,
height=2, width=3,
direction=LEFT,
)
bubble.write("So is\\\\this one")
bubble.content.scale(0.8)
bubble.content.set_color(BLACK)
bubble.set_color(BLACK)
bubble.set_fill(opacity=0)
randy.set_stroke(BLACK, 5, background=True)
self.add(randy, bubble, bubble.content)
class PiGesture3(Scene):
def construct(self):
randy = Randolph(mode="hooray", height=2)
randy.flip()
bubble = randy.get_bubble(
bubble_class=SpeechBubble,
height=2, width=3,
direction=LEFT,
)
bubble.write("And this\\\\one")
bubble.content.scale(0.8)
bubble.content.set_color(BLACK)
bubble.set_color(BLACK)
bubble.set_fill(opacity=0)
randy.set_stroke(BLACK, 5, background=True)
self.add(randy, bubble, bubble.content)
class AppleGoogleCoop(Scene):
def construct(self):
logos = Group(
self.get_apple_logo(),
self.get_google_logo(),
)
for logo in logos:
logo.set_height(2)
apple, google = logos
logos.arrange(RIGHT, buff=3)
arrows = VGroup()
for vect, u in zip([UP, DOWN], [0, 1]):
m1, m2 = logos[u], logos[1 - u]
arrows.add(Arrow(
m1.get_edge_center(vect),
m2.get_edge_center(vect),
path_arc=-90 * DEGREES,
buff=MED_LARGE_BUFF,
stroke_width=10,
))
self.play(LaggedStart(
Write(apple),
FadeIn(google),
lag_ratio=0.7,
))
self.wait()
self.play(ShowCreation(arrows, run_time=2))
self.wait()
def get_apple_logo(self):
result = SVGMobject("apple_logo")
result.set_color("#b3b3b3")
return result
def get_google_logo(self):
result = ImageMobject("google_logo_black")
return result
class LocationTracking(Scene):
def construct(self):
question = TextMobject(
"Would you like this company to track\\\\",
"and occasionally sell your location?"
)
question.to_edge(UP, buff=LARGE_BUFF)
slider = Rectangle(width=1.25, height=0.5)
slider.round_corners(radius=0.25)
slider.set_fill(GREEN, 1)
slider.next_to(question, DOWN, buff=MED_LARGE_BUFF)
dot = Dot(radius=0.25)
dot.set_fill(GREY_C, 1)
dot.set_stroke(WHITE, 3)
dot.move_to(slider, RIGHT)
morty = Mortimer()
morty.next_to(slider, RIGHT)
morty.to_edge(DOWN)
bubble = morty.get_bubble(
height=2,
width=3,
direction=LEFT,
)
answer = TextMobject("Um...", "no.")
answer.set_height(0.4)
answer.set_color(YELLOW)
bubble.add_content(answer)
self.add(morty)
self.play(
FadeInFromDown(question),
Write(slider),
FadeIn(dot),
)
self.play(morty.change, "confused", slider)
self.play(Blink(morty))
self.play(
FadeIn(bubble),
Write(answer[0]),
)
self.wait()
self.play(
dot.move_to, slider, LEFT,
slider.set_fill, {"opacity": 0},
FadeIn(answer[1]),
morty.change, "sassy"
)
self.play(Blink(morty))
self.wait(2)
self.play(Blink(morty))
self.wait(2)
class MoreLinks(Scene):
def construct(self):
words = TextMobject("See more links\\\\in the description.")
words.scale(2)
words.to_edge(UP, buff=2)
arrows = VGroup(*[
Vector(1.5 * DOWN, stroke_width=10)
for x in range(4)
])
arrows.arrange(RIGHT, buff=0.75)
arrows.next_to(words, DOWN, buff=0.5)
for arrow, color in zip(arrows, [BLUE_D, BLUE_C, BLUE_E, GREY_BROWN]):
arrow.set_color(color)
self.play(Write(words))
self.play(LaggedStartMap(ShowCreation, arrows))
self.wait()
class LDMEndScreen(PatreonEndScreen):
CONFIG = {
"scroll_time": 20,
"specific_patrons": [
"1stViewMaths",
"Aaron",
"Adam Dřínek",
"Adam Margulies",
"Aidan Shenkman",
"Alan Stein",
"Albin Egasse",
"Alex Mijalis",
"Alexander Mai",
"Alexis Olson",
"Ali Yahya",
"Andreas Snekloth Kongsgaard",
"Andrew Busey",
"Andrew Cary",
"Andrew R. Whalley",
"Aravind C V",
"Arjun Chakroborty",
"Arthur Zey",
"Ashwin Siddarth",
"Augustine Lim",
"Austin Goodman",
"Avi Finkel",
"Awoo",
"Axel Ericsson",
"Ayan Doss",
"AZsorcerer",
"Barry Fam",
"Bartosz Burclaf",
"Ben Delo",
"Benjamin Bailey",
"Bernd Sing",
"Bill Gatliff",
"Boris Veselinovich",
"Bradley Pirtle",
"Brandon Huang",
"Brendan Shah",
"Brian Cloutier",
"Brian Staroselsky",
"Britt Selvitelle",
"Britton Finley",
"Burt Humburg",
"Calvin Lin",
"Carl-Johan R. Nordangård",
"Charles Southerland",
"Charlie N",
"Chris Connett",
"Chris Druta",
"Christian Kaiser",
"cinterloper",
"Clark Gaebel",
"Colwyn Fritze-Moor",
"Corey Ogburn",
"D. Sivakumar",
"Dan Herbatschek",
"Daniel Brown",
"Daniel Herrera C",
"Darrell Thomas",
"Dave B",
"Dave Cole",
"Dave Kester",
"dave nicponski",
"David B. Hill",
"David Clark",
"David Gow",
"Delton Ding",
"Dominik Wagner",
"Eduardo Rodriguez",
"Emilio Mendoza",
"emptymachine",
"Eric Younge",
"Eryq Ouithaqueue",
"Federico Lebron",
"Fernando Via Canel",
"Frank R. Brown, Jr.",
"gary",
"Giovanni Filippi",
"Goodwine",
"Hal Hildebrand",
"Heptonion",
"Hitoshi Yamauchi",
"Isaac Gubernick",
"Ivan Sorokin",
"Jacob Baxter",
"Jacob Harmon",
"Jacob Hartmann",
"Jacob Magnuson",
"Jalex Stark",
"Jameel Syed",
"James Beall",
"Jason Hise",
"Jayne Gabriele",
"Jean-Manuel Izaret",
"Jeff Dodds",
"Jeff Linse",
"Jeff Straathof",
"Jeffrey Wolberg",
"Jimmy Yang",
"Joe Pregracke",
"Johan Auster",
"John C. Vesey",
"John Camp",
"John Haley",
"John Le",
"John Luttig",
"John Rizzo",
"John V Wertheim",
"jonas.app",
"Jonathan Heckerman",
"Jonathan Wilson",
"Joseph John Cox",
"Joseph Kelly",
"Josh Kinnear",
"Joshua Claeys",
"Joshua Ouellette",
"Juan Benet",
"Julien Dubois",
"Kai-Siang Ang",
"Kanan Gill",
"Karl Niu",
"Kartik Cating-Subramanian",
"Kaustuv DeBiswas",
"Killian McGuinness",
"kkm",
"Klaas Moerman",
"Kristoffer Börebäck",
"Kros Dai",
"L0j1k",
"Lael S Costa",
"LAI Oscar",
"Lambda GPU Workstations",
"Laura Gast",
"Lee Redden",
"Linh Tran",
"Luc Ritchie",
"Ludwig Schubert",
"Lukas Biewald",
"Lukas Zenick",
"Magister Mugit",
"Magnus Dahlström",
"Magnus Hiie",
"Manoj Rewatkar - RITEK SOLUTIONS",
"Mark B Bahu",
"Mark Heising",
"Mark Hopkins",
"Mark Mann",
"Martin Price",
"Mathias Jansson",
"Matt Godbolt",
"Matt Langford",
"Matt Roveto",
"Matt Russell",
"Matteo Delabre",
"Matthew Bouchard",
"Matthew Cocke",
"Maxim Nitsche",
"Michael Bos",
"Michael Hardel",
"Michael W White",
"Mirik Gogri",
"Molly Mackinlay",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nero Li",
"Nicholas Cahill",
"Nikita Lesnikov",
"Nitu Kitchloo",
"Oleg Leonov",
"Oliver Steele",
"Omar Zrien",
"Omer Tuchfeld",
"Patrick Gibson",
"Patrick Lucas",
"Pavel Dubov",
"Pesho Ivanov",
"Petar Veličković",
"Peter Ehrnstrom",
"Peter Francis",
"Peter Mcinerney",
"Pierre Lancien",
"Pradeep Gollakota",
"Rafael Bove Barrios",
"Raghavendra Kotikalapudi",
"Randy C. Will",
"rehmi post",
"Rex Godby",
"Ripta Pasay",
"Rish Kundalia",
"Roman Sergeychik",
"Roobie",
"Ryan Atallah",
"Samuel Judge",
"SansWord Huang",
"Scott Gray",
"Scott Walter, Ph.D.",
"soekul",
"Solara570",
"Spyridon Michalakis",
"Stephen Shanahan",
"Steve Huynh",
"Steve Muench",
"Steve Sperandeo",
"Steven Siddals",
"Stevie Metke",
"Sundar Subbarayan",
"supershabam",
"Suteerth Vishnu",
"Suthen Thomas",
"Tal Einav",
"Taras Bobrovytsky",
"Tauba Auerbach",
"Ted Suzman",
"Terry Hayes",
"THIS IS THE point OF NO RE tUUurRrhghgGHhhnnn",
"Thomas J Sargent",
"Thomas Tarler",
"Tianyu Ge",
"Tihan Seale",
"Tim Erbes",
"Tim Kazik",
"Tomasz Legutko",
"Tyler Herrmann",
"Tyler Parcell",
"Tyler VanValkenburg",
"Tyler Veness",
"Ubiquity Ventures",
"Vassili Philippov",
"Vasu Dubey",
"Veritasium",
"Vignesh Ganapathi Subramanian",
"Vinicius Reis",
"Vladimir Solomatin",
"Wooyong Ee",
"Xuanji Li",
"Yana Chernobilsky",
"Yavor Ivanov",
"Yetinother",
"YinYangBalance.Asia",
"Yu Jun",
"Yurii Monastyrshyn",
"Zachariah Rosenberg",
],
}

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,
]

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