mirror of
https://github.com/3b1b/manim.git
synced 2025-09-19 04:41:56 +00:00
rebase onto upstream
This commit is contained in:
commit
0edb4edfd0
36 changed files with 1348 additions and 32 deletions
|
@ -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
|
||||
|
|
19
README.md
19
README.md
|
@ -1,4 +1,5 @@
|
|||
# Manim - Mathematical Animation Engine
|
||||
<img src="logo/cropped.png"/>
|
||||
|
||||
[](https://manim.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://travis-ci.org/3b1b/manim)
|
||||
[](http://choosealicense.com/licenses/mit/)
|
||||
|
@ -87,15 +88,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.
|
||||
|
||||
|
|
13
active_projects/ode/all_part3_scenes.py
Normal file
13
active_projects/ode/all_part3_scenes.py
Normal 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,
|
||||
]
|
40
active_projects/ode/name_animations.py
Normal file
40
active_projects/ode/name_animations.py
Normal 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
|
|
@ -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
|
||||
|
|
427
active_projects/ode/part3/staging.py
Normal file
427
active_projects/ode/part3/staging.py
Normal 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
|
261
active_projects/ode/part3/temperature_graphs.py
Normal file
261
active_projects/ode/part3/temperature_graphs.py
Normal 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()
|
|
@ -4,8 +4,8 @@
|
|||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
|
@ -16,4 +16,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)
|
||||
|
|
|
@ -7,8 +7,8 @@ REM Command file for Sphinx documentation
|
|||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
|
|
11
docs/source/about.rst
Normal file
11
docs/source/about.rst
Normal 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
52
docs/source/conf.py
Normal 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']
|
4
docs/source/getting_started/animating_mobjects.rst
Normal file
4
docs/source/getting_started/animating_mobjects.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
Animating Mobjects
|
||||
==================
|
||||
|
||||
Learn about animations.
|
18
docs/source/getting_started/index.rst
Normal file
18
docs/source/getting_started/index.rst
Normal 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
|
132
docs/source/getting_started/learning_by_example.rst
Normal file
132
docs/source/getting_started/learning_by_example.rst
Normal 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`.
|
4
docs/source/getting_started/making_a_scene.rst
Normal file
4
docs/source/getting_started/making_a_scene.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
Making a Scene
|
||||
==============
|
||||
|
||||
Talk about Scenes and organization, bring it all together.
|
13
docs/source/getting_started/mathematical_objects.rst
Normal file
13
docs/source/getting_started/mathematical_objects.rst
Normal 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``
|
25
docs/source/index.rst
Normal file
25
docs/source/index.rst
Normal file
|
@ -0,0 +1,25 @@
|
|||
.. 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!
|
||||
=================================
|
||||
|
||||
test change
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
about
|
||||
installation/index
|
||||
getting_started/index
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
12
docs/source/installation/index.rst
Normal file
12
docs/source/installation/index.rst
Normal file
|
@ -0,0 +1,12 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
Instructions on installing Manim
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
linux
|
||||
mac
|
||||
windows
|
4
docs/source/installation/linux.rst
Normal file
4
docs/source/installation/linux.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
Linux
|
||||
=====
|
||||
|
||||
A stub for linux installation
|
4
docs/source/installation/mac.rst
Normal file
4
docs/source/installation/mac.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
Mac
|
||||
===
|
||||
|
||||
A stub for mac installation
|
4
docs/source/installation/windows.rst
Normal file
4
docs/source/installation/windows.rst
Normal file
|
@ -0,0 +1,4 @@
|
|||
Windows
|
||||
=======
|
||||
|
||||
A stub for windows installation
|
|
@ -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,
|
||||
|
|
BIN
logo/cropped.png
Normal file
BIN
logo/cropped.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
BIN
logo/graph.png
Normal file
BIN
logo/graph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
176
logo/logo.py
Normal file
176
logo/logo.py
Normal file
|
@ -0,0 +1,176 @@
|
|||
from manimlib.imports import *
|
||||
|
||||
NEW_BLUE = "#68a8e1"
|
||||
|
||||
class Thumbnail(GraphScene):
|
||||
CONFIG = {
|
||||
"y_max": 8,
|
||||
"y_axis_height": 5,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.show_function_graph()
|
||||
|
||||
def show_function_graph(self):
|
||||
self.setup_axes(animate=False)
|
||||
def func(x):
|
||||
return 0.1 * (x + 3-5) * (x - 3-5) * (x-5) + 5
|
||||
|
||||
def rect(x):
|
||||
return 2.775*(x-1.5)+3.862
|
||||
recta = self.get_graph(rect,x_min=-1,x_max=5)
|
||||
graph = self.get_graph(func,x_min=0.2,x_max=9)
|
||||
graph.set_color(NEW_BLUE)
|
||||
input_tracker_p1 = ValueTracker(1.5)
|
||||
input_tracker_p2 = ValueTracker(3.5)
|
||||
|
||||
def get_x_value(input_tracker):
|
||||
return input_tracker.get_value()
|
||||
|
||||
def get_y_value(input_tracker):
|
||||
return graph.underlying_function(get_x_value(input_tracker))
|
||||
|
||||
def get_x_point(input_tracker):
|
||||
return self.coords_to_point(get_x_value(input_tracker), 0)
|
||||
|
||||
def get_y_point(input_tracker):
|
||||
return self.coords_to_point(0, get_y_value(input_tracker))
|
||||
|
||||
def get_graph_point(input_tracker):
|
||||
return self.coords_to_point(get_x_value(input_tracker), get_y_value(input_tracker))
|
||||
|
||||
def get_v_line(input_tracker):
|
||||
return DashedLine(get_x_point(input_tracker), get_graph_point(input_tracker), stroke_width=2)
|
||||
|
||||
def get_h_line(input_tracker):
|
||||
return DashedLine(get_graph_point(input_tracker), get_y_point(input_tracker), stroke_width=2)
|
||||
#
|
||||
input_triangle_p1 = RegularPolygon(n=3, start_angle=TAU / 4)
|
||||
output_triangle_p1 = RegularPolygon(n=3, start_angle=0)
|
||||
for triangle in input_triangle_p1, output_triangle_p1:
|
||||
triangle.set_fill(WHITE, 1)
|
||||
triangle.set_stroke(width=0)
|
||||
triangle.scale(0.1)
|
||||
#
|
||||
input_triangle_p2 = RegularPolygon(n=3, start_angle=TAU / 4)
|
||||
output_triangle_p2 = RegularPolygon(n=3, start_angle=0)
|
||||
for triangle in input_triangle_p2, output_triangle_p2:
|
||||
triangle.set_fill(WHITE, 1)
|
||||
triangle.set_stroke(width=0)
|
||||
triangle.scale(0.1)
|
||||
|
||||
#
|
||||
x_label_p1 = TexMobject("a")
|
||||
output_label_p1 = TexMobject("f(a)")
|
||||
x_label_p2 = TexMobject("b")
|
||||
output_label_p2 = TexMobject("f(b)")
|
||||
v_line_p1 = get_v_line(input_tracker_p1)
|
||||
v_line_p2 = get_v_line(input_tracker_p2)
|
||||
h_line_p1 = get_h_line(input_tracker_p1)
|
||||
h_line_p2 = get_h_line(input_tracker_p2)
|
||||
graph_dot_p1 = Dot(color=WHITE)
|
||||
graph_dot_p2 = Dot(color=WHITE)
|
||||
|
||||
# reposition mobjects
|
||||
x_label_p1.next_to(v_line_p1, DOWN)
|
||||
x_label_p2.next_to(v_line_p2, DOWN)
|
||||
output_label_p1.next_to(h_line_p1, LEFT)
|
||||
output_label_p2.next_to(h_line_p2, LEFT)
|
||||
input_triangle_p1.next_to(v_line_p1, DOWN, buff=0)
|
||||
input_triangle_p2.next_to(v_line_p2, DOWN, buff=0)
|
||||
output_triangle_p1.next_to(h_line_p1, LEFT, buff=0)
|
||||
output_triangle_p2.next_to(h_line_p2, LEFT, buff=0)
|
||||
graph_dot_p1.move_to(get_graph_point(input_tracker_p1))
|
||||
graph_dot_p2.move_to(get_graph_point(input_tracker_p2))
|
||||
|
||||
|
||||
#
|
||||
self.play(
|
||||
ShowCreation(graph),
|
||||
)
|
||||
# Animacion del punto a
|
||||
self.add_foreground_mobject(graph_dot_p1)
|
||||
self.add_foreground_mobject(graph_dot_p2)
|
||||
self.play(
|
||||
DrawBorderThenFill(input_triangle_p1),
|
||||
Write(x_label_p1),
|
||||
ShowCreation(v_line_p1),
|
||||
GrowFromCenter(graph_dot_p1),
|
||||
ShowCreation(h_line_p1),
|
||||
Write(output_label_p1),
|
||||
DrawBorderThenFill(output_triangle_p1),
|
||||
DrawBorderThenFill(input_triangle_p2),
|
||||
Write(x_label_p2),
|
||||
ShowCreation(v_line_p2),
|
||||
GrowFromCenter(graph_dot_p2),
|
||||
ShowCreation(h_line_p2),
|
||||
Write(output_label_p2),
|
||||
DrawBorderThenFill(output_triangle_p2),
|
||||
run_time=0.5
|
||||
)
|
||||
self.add(
|
||||
input_triangle_p2,
|
||||
x_label_p2,
|
||||
graph_dot_p2,
|
||||
v_line_p2,
|
||||
h_line_p2,
|
||||
output_triangle_p2,
|
||||
output_label_p2,
|
||||
)
|
||||
###################
|
||||
pendiente_recta = self.get_secant_slope_group(
|
||||
1.9, recta, dx = 1.4,
|
||||
df_label = None,
|
||||
dx_label = None,
|
||||
dx_line_color = PURPLE,
|
||||
df_line_color= ORANGE,
|
||||
)
|
||||
grupo_secante = self.get_secant_slope_group(
|
||||
1.5, graph, dx = 2,
|
||||
df_label = None,
|
||||
dx_label = None,
|
||||
dx_line_color = "#942357",
|
||||
df_line_color= "#3f7d5c",
|
||||
secant_line_color = RED,
|
||||
)
|
||||
|
||||
|
||||
self.add(
|
||||
input_triangle_p2,
|
||||
graph_dot_p2,
|
||||
v_line_p2,
|
||||
h_line_p2,
|
||||
output_triangle_p2,
|
||||
)
|
||||
self.play(FadeIn(grupo_secante))
|
||||
|
||||
kwargs = {
|
||||
"x_min" : 4,
|
||||
"x_max" : 9,
|
||||
"fill_opacity" : 0.75,
|
||||
"stroke_width" : 0.25,
|
||||
}
|
||||
self.graph=graph
|
||||
iteraciones=6
|
||||
|
||||
|
||||
self.rect_list = self.get_riemann_rectangles_list(
|
||||
graph, iteraciones,start_color=PURPLE,end_color=ORANGE, **kwargs
|
||||
)
|
||||
flat_rects = self.get_riemann_rectangles(
|
||||
self.get_graph(lambda x : 0), dx = 0.5,start_color=invert_color(PURPLE),end_color=invert_color(ORANGE),**kwargs
|
||||
)
|
||||
rects = self.rect_list[0]
|
||||
self.transform_between_riemann_rects(
|
||||
flat_rects, rects,
|
||||
replace_mobject_with_target_in_scene = True,
|
||||
run_time=0.9
|
||||
)
|
||||
|
||||
# adding manim
|
||||
picture = Group(*self.mobjects)
|
||||
picture.scale(0.6).to_edge(LEFT, buff=SMALL_BUFF)
|
||||
manim = TextMobject("Manim").set_height(1.5) \
|
||||
.next_to(picture, RIGHT) \
|
||||
.shift(DOWN * 0.7)
|
||||
self.add(manim)
|
BIN
logo/with_name.png
Normal file
BIN
logo/with_name.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
BIN
logo/with_subtext.png
Normal file
BIN
logo/with_subtext.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 158 KiB |
|
@ -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
|
||||
|
||||
|
|
|
@ -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": [],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
10
travis/build_docs.sh
Executable 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
BIN
travis/crypt.enc
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue