40 Commits

Author SHA1 Message Date
Devin Neal
47a4a1f2df Add social badges README 2019-05-28 01:39:06 -07:00
Devin Neal
486b28e4d2 Merge pull request #557 from Kolloom/master
Add installation documentation for ubuntu
2019-05-28 01:09:00 -07:00
Devin Neal
2ce766b3bf Update docs badge 2019-05-28 01:01:48 -07:00
Devin Neal
2deeeab509 Merge pull request #558 from eulertour/master
Testing a PR making a change to the docs
2019-05-28 00:44:07 -07:00
Devin Neal
e93ef301ce test pr on docs 2019-05-28 00:39:00 -07:00
Devin Neal
c7b5aa6e05 change docs to upstream 2019-05-28 00:36:13 -07:00
Devin Neal
0edb4edfd0 rebase onto upstream 2019-05-28 00:33:05 -07:00
Devin Neal
78448b4388 make build script executable 2019-05-28 00:21:10 -07:00
Devin Neal
969bcf4154 refactor and test doc deployment 2019-05-28 00:13:27 -07:00
Devin Neal
f3f0e3ba03 third attempt at deploying docs 2019-05-27 23:58:05 -07:00
Devin Neal
aa2734477a second attempt at deploying docs 2019-05-27 23:39:00 -07:00
Devin Neal
d0eb2a0ce8 Merge branch 'master' of github.com:3b1b/manim 2019-05-27 23:16:40 -07:00
Devin Neal
a4c8302c55 first attempt at deploying docs 2019-05-27 23:08:43 -07:00
Kolloom
5d4897bd50 Add installation documentation for ubuntu 2019-05-28 00:48:25 -05:00
Devin Neal
49d8276033 Merge pull request #555 from yoshiask/patch-3
Update pycairo requirement
2019-05-27 19:55:58 -07:00
Grant Sanderson
b7fcc68b55 Merge pull request #556 from 3b1b/diffyq
Diffyq
2019-05-27 19:51:21 -07:00
Grant Sanderson
e6eb4dd94f Beginning heat equation solution animations 2019-05-27 19:48:48 -07:00
Grant Sanderson
29424eb6b3 Added simple midpoint function 2019-05-27 19:48:33 -07:00
Grant Sanderson
d1e3e5ed20 Formatting correction 2019-05-27 19:48:14 -07:00
Grant Sanderson
828c3dcd7a Added c2p and p2c abbreviations to Axes 2019-05-27 19:47:57 -07:00
Yoshi Askharoun
17558a4bd5 Update pycairo requirement
Many issues, such as #535, stem from the odd installation procedure of cairo and pycairo on Windows. By changing the pycairo requirement in requirements.txt, pip will no longer attempt to uninstall the working version. This should have no effect on the functionality of manim, though new installations are likely to default to pycairo 1.18.0 now.
2019-05-27 19:00:48 -07:00
Devin Neal
304822fb8c Merge branch 'master' of github.com:3b1b/manim 2019-05-27 16:14:45 -07:00
Devin Neal
9fa350d906 clean up docs 2019-05-27 16:14:20 -07:00
kyarik
c62ba223b6 Fixed typo in the docs about page (#553) 2019-05-27 15:52:41 -07:00
Devin Neal
4447bbd016 add docs 2019-05-26 00:35:45 -07:00
Grant Sanderson
98696a29f8 Merge pull request #542 from kyarik/patch-1
Fixed typo in class name in example_scenes.py
2019-05-24 15:15:12 -07:00
Grant Sanderson
84514a1f6f Merge pull request #543 from kyarik/patch-2
Mentioned -r argument in example_scenes comment
2019-05-24 15:14:53 -07:00
Grant Sanderson
c21ea85ec9 Merge pull request #544 from kyarik/patch-3
Improved the "Using manim" section in the README
2019-05-24 15:14:21 -07:00
Grant Sanderson
cec7872f48 Merge pull request #547 from 3b1b/diffyq
Diffyq
2019-05-24 15:13:40 -07:00
Grant Sanderson
ce866e8222 Removed duplicate name_animations.py 2019-05-24 15:09:53 -07:00
Grant Sanderson
91b3abae4a Change big_ol_pile_of_imports to manimlib.imports 2019-05-24 15:08:02 -07:00
Grant Sanderson
6e6dd260da Merge branch 'master' of github.com:3b1b/manim into diffyq 2019-05-24 15:06:59 -07:00
Grant Sanderson
2d3493c3d5 Fourier series name animations 2019-05-24 15:06:52 -07:00
Grant Sanderson
c4449fdfb8 Small tweaks to Fourier series animations 2019-05-24 15:06:10 -07:00
Grant Sanderson
bf8f517b49 Changed SceneFileWriter.get_default_file_name 2019-05-24 15:05:20 -07:00
Grant Sanderson
b9822db5bf Changed thumbnail of nn1 2019-05-24 15:04:45 -07:00
kyarik
2ebcec4bbe Improved the "Using manim" section in the README
Improved the wording and presentation of the "Using manim" section in the README file.
2019-05-22 11:00:57 +02:00
kyarik
68961251a5 Mentioned -r argument in example_scenes comment
It is a good option to mention because the current default for a high quality 60fps video is 2560x1440.
So, if someone wants a 1920x1080 video, they should pass -r 1080.
2019-05-21 11:54:56 +02:00
kyarik
0f5cc33002 Fixed typo in class name in example_scenes.py
Changed UdatersExample to UpdatersExample.
2019-05-21 11:44:30 +02:00
Devin Neal
e59178dae7 copy docs 2019-05-12 23:40:41 -07:00
31 changed files with 1257 additions and 27 deletions

View File

@@ -4,6 +4,10 @@ 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
@@ -16,6 +20,8 @@ before_script:
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

View File

@@ -1,8 +1,10 @@
<img src="logo/cropped.png"/>
[![Documentation Status](https://readthedocs.org/projects/manim/badge/?version=latest)](https://manim.readthedocs.io/en/latest/?badge=latest)
[![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/)
[![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 Subreddit](https://img.shields.io/discord/581738731934056449.svg?label=discord)](https://discord.gg/mMRrZQW)
Manim is an animation engine for explanatory math videos. It's used to create precise animations programmatically, as seen in the videos at [3Blue1Brown](https://www.3blue1brown.com/).
@@ -88,15 +90,17 @@ Try running the following:
```sh
python3 -m manim example_scenes.py SquareToCircle -pl
```
The -p is for previewing, meaning the video file will automatically open when it is done rendering.
Use -l for a faster rendering at a lower quality.
Use -s to skip to the end and just show the final frame.
Use -n (number) to skip ahead to the n'th animation of a scene.
Use -f to show the file in finder (for osx)
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.
Set MEDIA_DIR environment variable to determine where image and animation files will be written.
Some other useful flags include:
Look through the old_projects folder to see the code for previous 3b1b videos. Note, however, that developments are often made to the library without considering backwards compatibility on those old_projects. To run them with a guarantee that they will work, you will have to go back to the commit which complete that project.
* `-s` to skip to the end and just show the final frame.
* `-n <number>` to skip ahead to the `n`'th animation of a scene.
* `-f` to show the file in finder (for OSX).
Set `MEDIA_DIR` environment variable to specify where the image and animation files will be written.
Look through the `old_projects` folder to see the code for previous 3b1b videos. Note, however, that developments are often made to the library without considering backwards compatibility with those old projects. To run an old project with a guarantee that it will work, you will have to go back to the commit which completed that project.
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.

View File

@@ -0,0 +1,13 @@
from active_projects.ode.part3.staging import *
from active_projects.ode.part3.temperature_graphs import *
OUTPUT_DIRECTORY = "ode/part3"
SCENES_IN_ORDER = [
FourierSeriesIllustraiton,
FourierNameIntro,
CircleAnimationOfF,
LastChapterWrapper,
ThreeMainObservations,
SimpleSinExpGraph,
]

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python
from manimlib.imports import *
from active_projects.ode.part2.fourier_series import FourierOfName
name_color_pairs = [
]
circle_counts = [
# 10,
# 25,
100,
]
if __name__ == "__main__":
for name, color in name_color_pairs:
for n_circles in circle_counts:
try:
first_name = name.split(" ")[0]
scene = FourierOfName(
name_text=name,
name_color=color,
n_circles=n_circles,
file_writer_config={
"write_to_movie": True,
"output_directory": os.path.join(
"patron_fourier_names",
first_name,
),
"file_name": "{}_Fouierified_{}_Separate_paths".format(
first_name,
n_circles
),
},
camera_config={
"frame_rate": 24,
},
)
except:
pass

View File

@@ -313,12 +313,7 @@ class FourierOfPiSymbol(FourierCirclesScene):
coefs = self.get_coefficients_of_path(path)
circles = self.get_circles(coefficients=coefs)
for k, circle in zip(it.count(1), circles):
circle.set_stroke(width=max(
1 / np.sqrt(k),
1,
))
self.set_decreasing_stroke_widths(circles)
# approx_path = self.get_circle_end_path(circles)
drawn_path = self.get_drawn_path(circles)
if self.start_drawn:
@@ -329,6 +324,14 @@ class FourierOfPiSymbol(FourierCirclesScene):
self.add(drawn_path)
self.wait(self.run_time)
def set_decreasing_stroke_widths(self, circles):
for k, circle in zip(it.count(1), circles):
circle.set_stroke(width=max(
1 / np.sqrt(k),
1,
))
return circles
def get_path(self):
tex_mob = TexMobject(self.tex)
tex_mob.set_height(6)
@@ -338,6 +341,51 @@ class FourierOfPiSymbol(FourierCirclesScene):
return path
class FourierOfName(FourierOfPiSymbol):
CONFIG = {
"n_circles": 100,
"name_color": WHITE,
"name_text": "Abc",
"time_per_symbol": 5,
"slow_factor": 1 / 5,
}
def construct(self):
name = TextMobject(self.name_text)
max_width = FRAME_WIDTH - 2
max_height = FRAME_HEIGHT - 2
name.set_width(max_width)
if name.get_height() > max_height:
name.set_height(max_height)
circles = VGroup(VectorizedPoint())
for path in name.family_members_with_points():
for subpath in path.get_subpaths():
sp_mob = VMobject()
sp_mob.set_points(subpath)
coefs = self.get_coefficients_of_path(sp_mob)
new_circles = self.get_circles(
coefficients=coefs
)
self.set_decreasing_stroke_widths(new_circles)
drawn_path = self.get_drawn_path(new_circles)
drawn_path.clear_updaters()
drawn_path.set_stroke(self.name_color, 3)
new_circles.suspend_updating()
self.play(ReplacementTransform(circles, new_circles))
new_circles.resume_updating()
circles = new_circles
self.play(
ShowCreation(drawn_path),
rate_func=linear,
run_time=self.time_per_symbol
)
circles.suspend_updating()
self.play(FadeOut(circles))
self.wait(3)
class FourierOfPiSymbol5(FourierOfPiSymbol):
CONFIG = {
"n_circles": 5,
@@ -479,7 +527,7 @@ class FourierNDQ(FourierOfTrebleClef):
def get_shape(self):
path = VMobject()
shape = TexMobject("Hayley")
shape = TexMobject("NDQ")
for sp in shape.family_members_with_points():
path.append_points(sp.points)
return path

View File

@@ -0,0 +1,427 @@
from manimlib.imports import *
from active_projects.ode.part2.fourier_series import FourierOfTrebleClef
class FourierNameIntro(Scene):
def construct(self):
self.show_two_titles()
self.transition_to_image()
self.show_paper()
def show_two_titles(self):
lt = TextMobject("Fourier", "Series")
rt = TextMobject("Fourier", "Transform")
lt_variants = VGroup(
TextMobject("Complex", "Fourier Series"),
TextMobject("Discrete", "Fourier Series"),
)
rt_variants = VGroup(
TextMobject("Discrete", "Fourier Transform"),
TextMobject("Fast", "Fourier Transform"),
TextMobject("Quantum", "Fourier Transform"),
)
titles = VGroup(lt, rt)
titles.scale(1.5)
for title, vect in (lt, LEFT), (rt, RIGHT):
title.move_to(vect * FRAME_WIDTH / 4)
title.to_edge(UP)
for title, variants in (lt, lt_variants), (rt, rt_variants):
title.save_state()
title.target = title.copy()
title.target.scale(1 / 1.5, about_edge=RIGHT)
for variant in variants:
variant.move_to(title.target, UR)
variant[0].set_color(YELLOW)
v_line = Line(UP, DOWN)
v_line.set_height(FRAME_HEIGHT)
v_line.set_stroke(WHITE, 2)
self.play(
FadeInFrom(lt, RIGHT),
ShowCreation(v_line)
)
self.play(
FadeInFrom(rt, LEFT),
)
# Edit in images of circle animations
# and clips from FT video
# for title, variants in (rt, rt_variants), (lt, lt_variants):
for title, variants in [(rt, rt_variants)]:
# Maybe do it for left variant, maybe not...
self.play(
MoveToTarget(title),
FadeInFrom(variants[0][0], LEFT)
)
for v1, v2 in zip(variants, variants[1:]):
self.play(
FadeOutAndShift(v1[0], UP),
FadeInFrom(v2[0], DOWN),
run_time=0.5,
)
self.wait(0.5)
self.play(
Restore(title),
FadeOut(variants[-1][0])
)
self.wait()
self.titles = titles
self.v_line = v_line
def transition_to_image(self):
titles = self.titles
v_line = self.v_line
image = ImageMobject("Joseph Fourier")
image.set_height(5)
image.to_edge(LEFT)
frame = Rectangle()
frame.replace(image, stretch=True)
name = TextMobject("Joseph", "Fourier")
fourier_part = name.get_part_by_tex("Fourier")
fourier_part.set_color(YELLOW)
F_sym = fourier_part[0]
name.match_width(image)
name.next_to(image, DOWN)
self.play(
ReplacementTransform(v_line, frame),
FadeIn(image),
FadeIn(name[0]),
*[
ReplacementTransform(
title[0].deepcopy(),
name[1]
)
for title in titles
],
titles.scale, 0.65,
titles.arrange, DOWN,
titles.next_to, image, UP,
)
self.wait()
big_F = F_sym.copy()
big_F.set_fill(opacity=0)
big_F.set_stroke(WHITE, 2)
big_F.set_height(3)
big_F.move_to(midpoint(
image.get_right(),
RIGHT_SIDE,
))
big_F.shift(DOWN)
equivalence = VGroup(
fourier_part.copy().scale(1.25),
TexMobject("\\Leftrightarrow").scale(1.5),
TextMobject("Break down into\\\\pure frequencies"),
)
equivalence.arrange(RIGHT)
equivalence.move_to(big_F)
equivalence.to_edge(UP)
self.play(
FadeIn(big_F),
TransformFromCopy(fourier_part, equivalence[0]),
Write(equivalence[1:]),
)
self.wait(3)
self.play(FadeOut(VGroup(big_F, equivalence)))
self.image = image
self.name = name
def show_paper(self):
image = self.image
paper = ImageMobject("Fourier paper")
paper.match_height(image)
paper.next_to(image, RIGHT, MED_LARGE_BUFF)
date = TexMobject("1822")
date.next_to(paper, DOWN)
date_rect = SurroundingRectangle(date)
date_rect.scale(0.3)
date_rect.set_color(RED)
date_rect.shift(1.37 * UP + 0.08 * LEFT)
date_arrow = Arrow(
date_rect.get_bottom(),
date.get_top(),
buff=SMALL_BUFF,
color=date_rect.get_color(),
)
heat_rect = SurroundingRectangle(
TextMobject("CHALEUR")
)
heat_rect.set_color(RED)
heat_rect.scale(0.6)
heat_rect.move_to(
paper.get_top() +
1.22 * DOWN + 0.37 * RIGHT
)
heat_word = TextMobject("Heat")
heat_word.scale(1.5)
heat_word.next_to(paper, UP)
heat_word.shift(paper.get_width() * RIGHT)
heat_arrow = Arrow(
heat_rect.get_top(),
heat_word.get_left(),
buff=0.1,
path_arc=-60 * DEGREES,
color=heat_rect.get_color(),
)
self.play(FadeInFrom(paper, LEFT))
self.play(
ShowCreation(date_rect),
)
self.play(
GrowFromPoint(date, date_arrow.get_start()),
ShowCreation(date_arrow),
)
self.wait(3)
# Insert animation of circles/sine waves
# approximating a square wave
self.play(
ShowCreation(heat_rect),
)
self.play(
GrowFromPoint(heat_word, heat_arrow.get_start()),
ShowCreation(heat_arrow),
)
self.wait(3)
class FourierSeriesIllustraiton(Scene):
CONFIG = {
"n_range": range(1, 31, 2),
}
def construct(self):
n_range = self.n_range
axes1 = Axes(
number_line_config={
"include_tip": False,
},
x_axis_config={
"tick_frequency": 1 / 4,
"unit_size": 4,
},
x_min=0,
x_max=1,
y_min=-1,
y_max=1,
)
axes2 = axes1.copy()
step_func = axes2.get_graph(
lambda x: (1 if x < 0.5 else -1),
discontinuities=[0.5],
color=YELLOW,
stroke_width=3,
)
dot = Dot(axes2.c2p(0.5, 0), color=step_func.get_color())
dot.scale(0.5)
step_func.add(dot)
axes2.add(step_func)
arrow = Arrow(LEFT, RIGHT, color=WHITE)
VGroup(axes1, arrow, axes2).arrange(RIGHT).shift(UP)
def generate_nth_func(n):
return lambda x: (4 / n / PI) * np.sin(TAU * n * x)
def generate_kth_partial_sum_func(k):
return lambda x: np.sum([
generate_nth_func(n)(x)
for n in n_range[:k]
])
sine_graphs = VGroup(*[
axes1.get_graph(generate_nth_func(n))
for n in n_range
])
sine_graphs.set_stroke(width=3)
sine_graphs.set_color_by_gradient(
BLUE, GREEN, RED, YELLOW, PINK,
BLUE, GREEN, RED, YELLOW, PINK,
)
partial_sums = VGroup(*[
axes1.get_graph(generate_kth_partial_sum_func(k + 1))
for k in range(len(n_range))
])
partial_sums.match_style(sine_graphs)
sum_tex = TexMobject(
"\\frac{4}{\\pi}"
"\\sum_{1, 3, 5, \\dots}"
"\\frac{1}{n} \\sin(2\\pi \\cdot n \\cdot x)"
)
sum_tex.next_to(partial_sums, DOWN, buff=0.7)
eq = TexMobject("=")
step_tex = TexMobject(
"""
1 \\quad \\text{if $x < 0.5$} \\\\
0 \\quad \\text{if $x = 0.5$} \\\\
-1 \\quad \\text{if $x > 0.5$} \\\\
"""
)
lb = Brace(step_tex, LEFT, buff=SMALL_BUFF)
step_tex.add(lb)
step_tex.next_to(axes2, DOWN, buff=MED_LARGE_BUFF)
eq.move_to(midpoint(
step_tex.get_left(),
sum_tex.get_right()
))
rects = it.chain(
[
SurroundingRectangle(sum_tex[0][i])
for i in [4, 6, 8]
],
it.cycle([None])
)
self.add(axes1, arrow, axes2)
self.add(step_func)
self.add(sum_tex, eq, step_tex)
curr_partial_sum = axes1.get_graph(lambda x: 0)
curr_partial_sum.set_stroke(width=1)
for sine_graph, partial_sum, rect in zip(sine_graphs, partial_sums, rects):
anims1 = [
ShowCreation(sine_graph)
]
partial_sum.set_stroke(BLACK, 4, background=True)
anims2 = [
curr_partial_sum.set_stroke,
{"width": 1, "opacity": 0.5},
curr_partial_sum.set_stroke,
{"width": 0, "background": True},
ReplacementTransform(
sine_graph, partial_sum,
remover=True
),
]
if rect:
rect.match_style(sine_graph)
anims1.append(ShowCreation(rect))
anims2.append(FadeOut(rect))
self.play(*anims1)
self.play(*anims2)
curr_partial_sum = partial_sum
class CircleAnimationOfF(FourierOfTrebleClef):
CONFIG = {
"height": 3,
"n_circles": 200,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
path = VMobject()
shape = TexMobject("F")
for sp in shape.family_members_with_points():
path.append_points(sp.points)
return path
class LastChapterWrapper(Scene):
def construct(self):
full_rect = FullScreenFadeRectangle(
fill_color=DARK_GREY,
fill_opacity=1,
)
rect = ScreenRectangle(height=6)
rect.set_stroke(WHITE, 2)
rect.set_fill(BLACK, 1)
title = TextMobject("Last chapter")
title.scale(2)
title.to_edge(UP)
rect.next_to(title, DOWN)
self.add(full_rect)
self.play(
FadeIn(rect),
Write(title, run_time=2),
)
self.wait()
class ThreeMainObservations(Scene):
def construct(self):
fourier = ImageMobject("Joseph Fourier")
fourier.set_height(5)
fourier.to_corner(DR)
fourier.shift(LEFT)
bubble = ThoughtBubble(
direction=RIGHT,
height=3,
width=4,
)
bubble.move_tip_to(fourier.get_corner(UL) + 0.5 * DR)
observations = VGroup(
TextMobject(
"1)",
# "Sine waves",
# "H",
# "Heat equation",
),
TextMobject(
"2)",
# "Linearity"
),
TextMobject(
"3)",
# "Any$^{*}$ function is\\\\",
# "a sum of sine waves",
),
)
# heart = SuitSymbol("hearts")
# heart.replace(observations[0][2])
# observations[0][2].become(heart)
# observations[0][1].add(happiness)
# observations[2][2].align_to(
# observations[2][1], LEFT,
# )
observations.arrange(
DOWN,
aligned_edge=LEFT,
buff=LARGE_BUFF,
)
observations.set_height(FRAME_HEIGHT - 2)
observations.to_corner(UL, buff=LARGE_BUFF)
self.add(fourier)
self.play(ShowCreation(bubble))
self.wait()
self.play(LaggedStart(*[
TransformFromCopy(bubble, observation)
for observation in observations
], lag_ratio=0.2))
self.play(
FadeOut(fourier),
FadeOut(bubble),
)
self.wait()
class NewSceneName(Scene):
def construct(self):
pass

View File

@@ -0,0 +1,261 @@
from manimlib.imports import *
class TemperatureGraphScene(SpecialThreeDScene):
CONFIG = {
"axes_config": {
"x_min": 0,
"x_max": TAU,
"y_min": 0,
"y_max": 10,
"z_min": -3,
"z_max": 3,
"x_axis_config": {
"tick_frequency": TAU / 8,
"include_tip": False,
},
"num_axis_pieces": 1,
},
"default_graph_style": {
"stroke_width": 2,
"stroke_color": WHITE,
"background_image_file": "VerticalTempGradient",
},
"default_surface_style": {
"fill_opacity": 0.1,
"checkerboard_colors": [LIGHT_GREY],
"stroke_width": 0.5,
"stroke_color": WHITE,
"stroke_opacity": 0.5,
},
}
def get_three_d_axes(self, include_labels=True):
axes = ThreeDAxes(**self.axes_config)
axes.set_stroke(width=2)
# Add number labels
# TODO?
# Add axis labels
if include_labels:
x_label = TexMobject("x")
x_label.next_to(axes.x_axis.get_right(), DOWN)
axes.x_axis.add(x_label)
t_label = TextMobject("Time")
t_label.rotate(90 * DEGREES, OUT)
t_label.next_to(axes.y_axis.get_top(), DL)
axes.y_axis.add(t_label)
temp_label = TextMobject("Temperature")
temp_label.rotate(90 * DEGREES, RIGHT)
temp_label.next_to(axes.z_axis.get_zenith(), RIGHT)
axes.z_axis.add(temp_label)
# Adjust axis orinetations
axes.x_axis.rotate(
90 * DEGREES, RIGHT,
about_point=axes.c2p(0, 0, 0),
)
axes.y_axis.rotate(
90 * DEGREES, UP,
about_point=axes.c2p(0, 0, 0),
)
# Add xy-plane
surface_config = {
"u_min": 0,
"u_max": axes.x_max,
"v_min": 0,
"v_max": axes.y_max,
"resolution": (16, 10),
}
axes.surface_config = surface_config
input_plane = ParametricSurface(
lambda x, t: axes.c2p(x, t, 0),
# lambda x, t: np.array([x, t, 0]),
**surface_config,
)
input_plane.set_style(
fill_opacity=0.5,
fill_color=BLUE_B,
stroke_width=0.5,
stroke_color=WHITE,
)
axes.input_plane = input_plane
return axes
def get_initial_state_graph(self, axes, func, **kwargs):
config = dict()
config.update(self.default_graph_style)
config.update(kwargs)
return ParametricFunction(
lambda x: axes.c2p(
x, 0, func(x)
),
t_min=axes.x_min,
t_max=axes.x_max,
**config,
)
def get_surface(self, axes, func, **kwargs):
config = dict()
config.update(axes.surface_config)
config.update(self.default_surface_style)
config.update(kwargs)
return ParametricSurface(
lambda x, t: axes.c2p(
x, t, func(x, t)
),
**config
)
def orient_three_d_mobject(self, mobject,
phi=85 * DEGREES,
theta=-80 * DEGREES):
mobject.rotate(-90 * DEGREES - theta, OUT)
mobject.rotate(phi, LEFT)
return mobject
class SimpleSinExpGraph(TemperatureGraphScene):
def construct(self):
axes = self.get_three_d_axes()
sine_graph = self.get_sine_graph(axes)
sine_exp_surface = self.get_sine_exp_surface(axes)
self.set_camera_orientation(
phi=80 * DEGREES,
theta=-80 * DEGREES,
)
self.camera.frame_center.shift(3 * RIGHT)
self.begin_ambient_camera_rotation(rate=0.01)
self.add(axes)
self.play(ShowCreation(sine_graph))
self.play(UpdateFromAlphaFunc(
sine_exp_surface,
lambda m, a: m.become(
self.get_sine_exp_surface(axes, v_max=a * 10)
),
run_time=3
))
self.wait(20)
#
def sin_exp(self, x, t, A=2, omega=1, k=0.25):
return A * np.sin(omega * x) * np.exp(-k * (omega**2) * t)
def get_sine_graph(self, axes, **config):
return self.get_initial_state_graph(
axes,
lambda x: self.sin_exp(x, 0),
**config
)
def get_sine_exp_surface(self, axes, **config):
return self.get_surface(
axes,
lambda x, t: self.sin_exp(x, t),
**config
)
class AddMultipleSolutions(SimpleSinExpGraph):
CONFIG = {
"axes_config": {
"x_axis_config": {
"unit_size": 0.7,
},
}
}
def construct(self):
axes1, axes2, axes3 = all_axes = VGroup(*[
self.get_three_d_axes(
include_labels=False,
)
for x in range(3)
])
all_axes.scale(0.5)
self.orient_three_d_mobject(all_axes)
As = [1.5, 1.5]
omegas = [1, 2]
ks = [0.25, 0.01]
quads = [
(axes1, [As[0]], [omegas[0]], [ks[0]]),
(axes2, [As[1]], [omegas[1]], [ks[1]]),
(axes3, As, omegas, ks),
]
for axes, As, omegas, ks in quads:
graph = self.get_initial_state_graph(
axes,
lambda x: np.sum([
self.sin_exp(x, 0, A, omega, k)
for A, omega, k in zip(As, omegas, ks)
])
)
surface = self.get_surface(
axes,
lambda x, t: np.sum([
self.sin_exp(x, t, A, omega)
for A, omega in zip(As, omegas)
])
)
surface.sort(lambda p: -p[2])
axes.add(surface, graph)
axes.graph = graph
axes.surface = surface
self.set_camera_orientation(distance=100)
plus = TexMobject("+").scale(2)
equals = TexMobject("=").scale(2)
group = VGroup(
axes1, plus, axes2, equals, axes3,
)
group.arrange(RIGHT, buff=SMALL_BUFF)
for axes in all_axes:
checkmark = TexMobject("\\checkmark")
checkmark.set_color(GREEN)
checkmark.scale(2)
checkmark.next_to(axes, UP)
checkmark.shift(0.7 * DOWN)
axes.checkmark = checkmark
self.add(axes1, axes2)
self.play(
LaggedStart(
Write(axes1.surface),
Write(axes2.surface),
),
LaggedStart(
FadeInFrom(axes1.checkmark, DOWN),
FadeInFrom(axes2.checkmark, DOWN),
),
lag_ratio=0.2,
run_time=1,
)
self.wait()
self.play(Write(plus))
self.play(
Transform(
axes1.copy().set_fill(opacity=0),
axes3
),
Transform(
axes2.copy().set_fill(opacity=0),
axes3
),
FadeInFrom(equals, LEFT)
)
self.play(
FadeInFrom(axes3.checkmark, DOWN),
)
self.wait()

19
docs/Makefile Normal file
View File

@@ -0,0 +1,19 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

35
docs/make.bat Normal file
View File

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

11
docs/source/about.rst Normal file
View File

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

52
docs/source/conf.py Normal file
View File

@@ -0,0 +1,52 @@
# 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('.'))
# -- Project information -----------------------------------------------------
project = 'Manim'
copyright = '2019, EulerTour'
author = 'EulerTour'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# 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 = ['_static']

View File

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

View File

@@ -0,0 +1,18 @@
Getting Started
===============
Todd Zimmerman put together `a very nice tutorial`_ on getting started with
``manim``, but is unfortunately outdated. It's still useful for understanding
how ``manim`` is used, but the examples won't run on the latest version of
``manim``.
.. _a very nice tutorial: https://talkingphysics.wordpress.com/2018/06/11/learning-how-to-animate-videos-using-``manim``-series-a-journey/
.. toctree::
:caption: Contents:
:maxdepth: 2
learning_by_example
mathematical_objects
animating_mobjects
making_a_scene

View File

@@ -0,0 +1,132 @@
Learning by Example
===================
You create videos in manim by writing :class:`~scene.scene.Scene` instances.
``example_scenes.py`` contains a few simple ones that we can use to learn about
manim. For instance, take ``SquareToCircle``.
.. code-block:: python
:linenos:
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))
:meth:`~scene.scene.Scene.construct` specifies what is displayed on the screen
when the :class:`~scene.scene.Scene` is rendered to video. You can render a
:class:`~scene.scene.Scene` by running ``extract_scene.py``. Run ``python
extract_scene.py -h`` to see how it's used.
.. code-block:: none
> python extract_scene.py -h
usage: extract_scene.py [-h] [-p] [-w] [-s] [-l] [-m] [-g] [-f] [-t] [-q] [-a]
[-o OUTPUT_NAME] [-n START_AT_ANIMATION_NUMBER]
[-r RESOLUTION] [-c COLOR] [-d OUTPUT_DIRECTORY]
file [scene_name]
positional arguments:
file path to file holding the python code for the scene
scene_name Name of the Scene class you want to see
optional arguments:
-h, --help show this help message and exit
-p, --preview
-w, --write_to_movie
-s, --show_last_frame
-l, --low_quality
-m, --medium_quality
-g, --save_pngs
-f, --show_file_in_finder
-t, --transparent
-q, --quiet
-a, --write_all
-o OUTPUT_NAME, --output_name OUTPUT_NAME
-n START_AT_ANIMATION_NUMBER, --start_at_animation_number START_AT_ANIMATION_NUMBER
-r RESOLUTION, --resolution RESOLUTION
-c COLOR, --color COLOR
-d OUTPUT_DIRECTORY, --output_directory OUTPUT_DIRECTORY
The most common flags are ``-p``, to automatically play the generated video,
``-l``, to render in lower quality in favor of speed, and ``-s``, to show the
last frame of the :class:`~scene.scene.Scene` for faster development. Run
``python extract_scene.py example_scenes.py SquareToCircle -pl`` to produce a
file called SquareToCircle.mp4 in the media directory that you have configured,
and automatically play it.
.. raw:: html
<iframe width="560" height="315" src="https://www.youtube.com/embed/8tvYDIGLJJA?ecver=1" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
Let's step through each line of the :class:`~scene.scene.Scene`. Lines 3 and 4
instantiate a :class:`~mobject.geometry.Circle` and
:class:`~mobject.geometry.Square`, respectively. Both of these subclass
:class:`~mobject.mobject.Mobject`, the base class for objects in manim. Note
that instantiating a :class:`~mobject.mobject.Mobject` does not add it to the
:class:`~scene.scene.Scene`, so you wouldn't see anything if you were to render
the :class:`~scene.scene.Scene` at this point.
.. code-block:: python
:linenos:
:lineno-start: 3
circle = Circle()
square = Square()
Lines 5, 6, and 7 apply various modifications to the mobjects before animating
them. The call to :meth:`~mobject.mobject.Mobject.flip` on line 5 flips the
:class:`~mobject.geometry.Square` across the RIGHT vector. This is equivalent
to a refection across the x-axis. Then the call to
:meth:`~mobject.mobject.Mobject.rotate` on line 6 rotates the
:class:`~mobject.geometry.Square` 3/8ths of a full rotation counterclockwise.
Finally, the call to :meth:`~mobject.mobject.Mobject.set_fill` on line 7 sets
the fill color for the :class:`~mobject.geometry.Circle` to pink, and its
opacity to 0.5.
.. code-block:: python
:linenos:
:lineno-start: 5
square.flip(RIGHT)
square.rotate(-3 * TAU / 8)
circle.set_fill(PINK, opacity=0.5)
Line 9 is the first to generate video.
:class:`~animation.creation.ShowCreation`,
:class:`~animation.transform.Transform`, and
:class:`~animation.creation.FadeOut` are
:class:`~animation.animation.Animation` instances. Each
:class:`~animation.animation.Animation` takes one or more
:class:`~mobject.mobject.Mobject` instances as arguments, which it animates
when passed to :meth:`~scene.scene.Scene.play`. This is how video is typically
created in manim. :class:`~mobject.mobject.Mobject` instances are automatically
added to the :class:`~scene.scene.Scene` when they are animated. You can add a
:class:`~mobject.mobject.Mobject` to the :class:`~scene.scene.Scene` manually
by passing it as an argument to :meth:`~scene.scene.Scene.add`.
.. code-block:: python
:linenos:
:lineno-start: 9
self.play(ShowCreation(square))
self.play(Transform(square, circle))
self.play(FadeOut(square))
:class:`~animation.creation.ShowCreation` draws a
:class:`~mobject.mobject.Mobject` to the screen,
:class:`~animation.transform.Transform` morphs one
:class:`~mobject.mobject.Mobject` into another, and
:class:`~animation.creation.FadeOut` fades a
:class:`~mobject.mobject.Mobject` out of the :class:`~scene.scene.Scene`. Note
that only the first argument to :class:`~animation.transform.Transform` is
modified, and the second is not added to the :class:`~scene.scene.Scene`. After
line 10 is executed ``square`` is a :class:`~mobject.geometry.Square` instance
with the shape of a :class:`~mobject.geometry.Circle`.

View File

@@ -0,0 +1,4 @@
Making a Scene
==============
Talk about Scenes and organization, bring it all together.

View File

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

23
docs/source/index.rst Normal file
View File

@@ -0,0 +1,23 @@
.. 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.
Welcome to Manim's documentation!
=================================
.. toctree::
:maxdepth: 2
:caption: Contents:
about
installation/index
getting_started/index
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
Mac
===
A stub for mac installation

View File

@@ -0,0 +1,4 @@
Windows
=======
A stub for windows installation

View File

@@ -11,6 +11,8 @@ from manimlib.imports import *
# Use the -p to have the animation (or image, if -s was
# used) pop up 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):
@@ -111,7 +113,7 @@ class WriteStuff(Scene):
self.wait()
class UdatersExample(Scene):
class UpdatersExample(Scene):
def construct(self):
decimal = DecimalNumber(
0,

View File

@@ -171,12 +171,18 @@ class Axes(VGroup, CoordinateSystem):
result += (axis.number_to_point(coord) - origin)
return result
def c2p(self, *coords):
return self.coords_to_point(*coords)
def point_to_coords(self, point):
return tuple([
axis.point_to_number(point)
for axis in self.get_axes()
])
def p2c(self, point):
return self.point_to_coords(point)
def get_axes(self):
return self.axes

View File

@@ -8,7 +8,7 @@ class ParametricFunction(VMobject):
CONFIG = {
"t_min": 0,
"t_max": 1,
"step_size": 0.01, # use "auto" (lwoercase) for automatic step size
"step_size": 0.01, # Use "auto" (lowercase) for automatic step size
"dt": 1e-8,
# TODO, be smarter about figuring these out?
"discontinuities": [],

View File

@@ -34,6 +34,7 @@ class SceneFileWriter(object):
# TODO, address this in extract_scene et. al.
"file_name": None,
"output_directory": None,
"file_name": None,
}
def __init__(self, scene, **kwargs):
@@ -81,7 +82,10 @@ class SceneFileWriter(object):
return root if root else ext[1:]
def get_default_file_name(self):
return self.scene.__class__.__name__
if self.file_name is None:
return self.scene.__class__.__name__
else:
return self.file_name
def get_movie_directory(self):
pixel_height = self.scene.camera.pixel_height

View File

@@ -205,6 +205,10 @@ def center_of_mass(points):
return sum(points) / len(points)
def midpoint(point1, point2):
return center_of_mass([point1, point2])
def line_intersection(line1, line2):
"""
return intersection point of two lines,

View File

@@ -20,7 +20,7 @@ import random
import numpy as np
from PIL import Image
from nn.mnist_loader import load_data_wrapper
from utils.space_ops import get_norm
# from utils.space_ops import get_norm
NN_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
# PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases_80")

View File

@@ -2964,7 +2964,7 @@ class BiasForInactiviyWords(Scene):
self.play(Write(words))
self.wait(3)
class ContinualEdgeUpdate(ContinualAnimation):
class ContinualEdgeUpdate(VGroup):
CONFIG = {
"max_stroke_width" : 3,
"stroke_width_exp" : 7,
@@ -2972,7 +2972,8 @@ class ContinualEdgeUpdate(ContinualAnimation):
"colors" : [GREEN, GREEN, GREEN, RED],
}
def __init__(self, network_mob, **kwargs):
digest_config(self, kwargs)
VGroup.__init__(self, **kwargs)
self.internal_time = 0
n_cycles = self.n_cycles
edges = VGroup(*it.chain(*network_mob.edge_groups))
self.move_to_targets = []
@@ -2990,11 +2991,14 @@ class ContinualEdgeUpdate(ContinualAnimation):
edge.generate_target()
edge.target.set_stroke(edge.colors[0], edge.widths[0])
self.move_to_targets.append(MoveToTarget(edge))
edge.become(edge.target)
self.move_to_targets.append(edge)
self.edges = edges
ContinualAnimation.__init__(self, edges, **kwargs)
self.add(edges)
self.add_updater(lambda m, dt: self.update_edges(dt))
def update_mobject(self, dt):
def update_edges(self, dt):
self.internal_time += dt
if self.internal_time < 1:
alpha = smooth(self.internal_time)
for move_to_target in self.move_to_targets:
@@ -4559,8 +4563,9 @@ class ShowAmplify(PiCreatureScene):
class Thumbnail(NetworkScene):
CONFIG = {
"network_mob_config" : {
'neuron_stroke_color' : WHITE
}
'neuron_stroke_color' : WHITE,
'layer_to_layer_buff': 1.25,
},
}
def construct(self):
network_mob = self.network_mob
@@ -4568,6 +4573,23 @@ class Thumbnail(NetworkScene):
for layer in network_mob.layers:
layer.neurons.set_stroke(width = 5)
network_mob.set_height(5)
network_mob.to_edge(DOWN)
network_mob.to_edge(LEFT, buff=1)
subtitle = TextMobject(
"From the\\\\",
"ground up\\\\",
)
# subtitle.arrange(
# DOWN,
# buff=0.25,
# aligned_edge=LEFT,
# )
subtitle.set_color(YELLOW)
subtitle.set_height(2.75)
subtitle.next_to(network_mob, RIGHT, buff=MED_LARGE_BUFF)
edge_update = ContinualEdgeUpdate(
network_mob,
max_stroke_width = 10,
@@ -4576,7 +4598,18 @@ class Thumbnail(NetworkScene):
edge_update.internal_time = 3
edge_update.update(0)
for mob in network_mob.family_members_with_points():
if mob.get_stroke_width() < 2:
mob.set_stroke(width=2)
title = TextMobject("Neural Networks")
title.scale(3)
title.to_edge(UP)
self.add(network_mob)
self.add(subtitle)
self.add(title)

View File

@@ -6,5 +6,5 @@ progressbar==2.5
scipy==1.1.0
tqdm==4.24.0
opencv-python==3.4.2.17
pycairo==1.17.1
pycairo>=1.17.1
pydub==0.23.0

10
travis/build_docs.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
pip install sphinx_rtd_theme
make --directory docs/ html
openssl aes-256-cbc -K $encrypted_1b28e850a424_key \
-iv $encrypted_1b28e850a424_iv \
-in travis/crypt.enc \
-out travis/crypt -d
tar xf travis/crypt
travis/deploy_docs.sh

BIN
travis/crypt.enc Normal file

Binary file not shown.