mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Beginning EoC Chapter 2
This commit is contained in:
parent
66dbe8aaae
commit
982ddb4c14
12 changed files with 599 additions and 89 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,5 +3,6 @@
|
||||||
homeless.py
|
homeless.py
|
||||||
ka_playgrounds/
|
ka_playgrounds/
|
||||||
playground.py
|
playground.py
|
||||||
|
special_animations.py
|
||||||
prettiness_hall_of_fame.py
|
prettiness_hall_of_fame.py
|
||||||
files/
|
files/
|
|
@ -11,24 +11,30 @@ from animation import Animation
|
||||||
|
|
||||||
class Rotating(Animation):
|
class Rotating(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"axes" : [RIGHT, UP],
|
"axes" : [],
|
||||||
"axis" : None,
|
"axis" : OUT,
|
||||||
"radians" : 2*np.pi,
|
"radians" : 2*np.pi,
|
||||||
"run_time" : 20.0,
|
"run_time" : 5,
|
||||||
"rate_func" : None,
|
"rate_func" : None,
|
||||||
"in_place" : True,
|
"in_place" : True,
|
||||||
|
"about_point" : None,
|
||||||
}
|
}
|
||||||
def update_submobject(self, submobject, starting_submobject, alpha):
|
def update_submobject(self, submobject, starting_submobject, alpha):
|
||||||
submobject.points = np.array(starting_submobject.points)
|
submobject.points = np.array(starting_submobject.points)
|
||||||
|
|
||||||
def update_mobject(self, alpha):
|
def update_mobject(self, alpha):
|
||||||
Animation.update_mobject(self, alpha)
|
Animation.update_mobject(self, alpha)
|
||||||
axes = [self.axis] if self.axis is not None else self.axes
|
axes = self.axes if self.axes else [self.axis]
|
||||||
if self.in_place:
|
about_point = None
|
||||||
method = self.mobject.rotate_in_place
|
if self.about_point is not None:
|
||||||
else:
|
about_point = self.about_point
|
||||||
method = self.mobject.rotate
|
elif self.in_place: #This is superseeded
|
||||||
method(alpha*self.radians, axes = axes)
|
self.about_point = self.mobject.get_center()
|
||||||
|
self.mobject.rotate(
|
||||||
|
alpha*self.radians,
|
||||||
|
axes = axes,
|
||||||
|
about_point = self.about_point
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ShowPartial(Animation):
|
class ShowPartial(Animation):
|
||||||
|
@ -267,8 +273,14 @@ class Succession(Animation):
|
||||||
last_anim = self.anims[self.last_index]
|
last_anim = self.anims[self.last_index]
|
||||||
last_anim.clean_up()
|
last_anim.clean_up()
|
||||||
if last_anim.mobject is curr_anim.mobject:
|
if last_anim.mobject is curr_anim.mobject:
|
||||||
self.anims[index].starting_mobject = curr_anim.mobject.copy()
|
#TODO, is there a way to do this that doesn't
|
||||||
self.anims[index].update_mobject(scaled_alpha - index)
|
#require leveraging implementation details of
|
||||||
|
#Animations, and knowing about the different
|
||||||
|
#struction of Transform?
|
||||||
|
if hasattr(curr_anim, "ending_mobject"):
|
||||||
|
curr_anim.mobject.align_data(curr_anim.ending_mobject)
|
||||||
|
curr_anim.starting_mobject = curr_anim.mobject.copy()
|
||||||
|
curr_anim.update(scaled_alpha - index)
|
||||||
self.last_index = index
|
self.last_index = index
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -253,20 +253,23 @@ class OpeningQuote(Scene):
|
||||||
self.play(Write(author, run_time = 3))
|
self.play(Write(author, run_time = 3))
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
def get_quote(self):
|
def get_quote(self, max_width = 2*SPACE_WIDTH-1):
|
||||||
if isinstance(self.quote, str):
|
if isinstance(self.quote, str):
|
||||||
quote = TextMobject(
|
quote = TextMobject(
|
||||||
"``%s''"%self.quote.strip(),
|
"``%s''"%self.quote.strip(),
|
||||||
alignment = "",
|
alignment = "",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
words = list(self.quote)
|
words = ["``"] + list(self.quote) + ["''"]
|
||||||
words[0] = "``%s"%words[0]
|
|
||||||
words[-1] += "''"
|
|
||||||
quote = TextMobject(*words, alignment = "")
|
quote = TextMobject(*words, alignment = "")
|
||||||
|
##TODO, make less hacky
|
||||||
|
quote[0].shift(0.2*RIGHT)
|
||||||
|
quote[-1].shift(0.2*LEFT)
|
||||||
for term, color in self.highlighted_quote_terms.items():
|
for term, color in self.highlighted_quote_terms.items():
|
||||||
quote.highlight_by_tex(term, color)
|
quote.highlight_by_tex(term, color)
|
||||||
quote.to_edge(UP)
|
quote.to_edge(UP)
|
||||||
|
if quote.get_width() > max_width:
|
||||||
|
quote.scale_to_fit_width(max_width)
|
||||||
return quote
|
return quote
|
||||||
|
|
||||||
def get_author(self, quote):
|
def get_author(self, quote):
|
||||||
|
|
425
eoc/chapter2.py
Normal file
425
eoc/chapter2.py
Normal file
|
@ -0,0 +1,425 @@
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
from mobject.tex_mobject import TexMobject
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.image_mobject import ImageMobject
|
||||||
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
|
from animation.animation import Animation
|
||||||
|
from animation.transform import *
|
||||||
|
from animation.simple_animations import *
|
||||||
|
from animation.playground import *
|
||||||
|
from topics.geometry import *
|
||||||
|
from topics.characters import *
|
||||||
|
from topics.functions import *
|
||||||
|
from topics.fractals import *
|
||||||
|
from topics.number_line import *
|
||||||
|
from topics.combinatorics import *
|
||||||
|
from topics.numerals import *
|
||||||
|
from topics.three_dimensions import *
|
||||||
|
from topics.objects import *
|
||||||
|
from scene import Scene
|
||||||
|
from scene.zoomed_scene import ZoomedScene
|
||||||
|
from camera import Camera
|
||||||
|
from mobject.svg_mobject import *
|
||||||
|
from mobject.tex_mobject import *
|
||||||
|
|
||||||
|
from eoc.chapter1 import OpeningQuote
|
||||||
|
from eoc.graph_scene import *
|
||||||
|
|
||||||
|
|
||||||
|
class Car(SVGMobject):
|
||||||
|
CONFIG = {
|
||||||
|
"file_name" : "Car",
|
||||||
|
"height" : 1,
|
||||||
|
"color" : GREY,
|
||||||
|
}
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
SVGMobject.__init__(self, **kwargs)
|
||||||
|
self.scale_to_fit_height(self.height)
|
||||||
|
self.set_stroke(color = WHITE, width = 0)
|
||||||
|
self.set_fill(self.color, opacity = 1)
|
||||||
|
|
||||||
|
randy = Randolph(mode = "happy")
|
||||||
|
randy.scale_to_fit_height(0.6*self.get_height())
|
||||||
|
randy.stretch(0.8, 0)
|
||||||
|
randy.look(RIGHT)
|
||||||
|
randy.move_to(self)
|
||||||
|
randy.shift(0.07*self.height*(RIGHT+UP))
|
||||||
|
self.add_to_back(randy)
|
||||||
|
|
||||||
|
orientation_line = Line(self.get_left(), self.get_right())
|
||||||
|
orientation_line.set_stroke(width = 0)
|
||||||
|
self.add(orientation_line)
|
||||||
|
self.orientation_line = orientation_line
|
||||||
|
|
||||||
|
|
||||||
|
self.add_treds_to_tires()
|
||||||
|
|
||||||
|
def move_to(self, point_or_mobject):
|
||||||
|
vect = rotate_vector(
|
||||||
|
UP+LEFT, self.orientation_line.get_angle()
|
||||||
|
)
|
||||||
|
self.next_to(point_or_mobject, vect, buff = 0)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_front_line(self):
|
||||||
|
return DashedLine(
|
||||||
|
self.get_corner(UP+RIGHT),
|
||||||
|
self.get_corner(DOWN+RIGHT),
|
||||||
|
color = YELLOW,
|
||||||
|
dashed_segment_length = 0.05,
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_treds_to_tires(self):
|
||||||
|
for tire in self.get_tires():
|
||||||
|
radius = tire.get_width()/2
|
||||||
|
center = tire.get_center()
|
||||||
|
tred = Line(
|
||||||
|
0.9*radius*RIGHT, 1.4*radius*RIGHT,
|
||||||
|
stroke_width = 2,
|
||||||
|
color = BLACK
|
||||||
|
)
|
||||||
|
tred.rotate_in_place(np.pi/4)
|
||||||
|
for theta in np.arange(0, 2*np.pi, np.pi/4):
|
||||||
|
new_tred = tred.copy()
|
||||||
|
new_tred.rotate(theta)
|
||||||
|
new_tred.shift(center)
|
||||||
|
tire.add(new_tred)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_tires(self):
|
||||||
|
return self[1][1], self[1][3]
|
||||||
|
|
||||||
|
class MoveCar(ApplyMethod):
|
||||||
|
def __init__(self, car, target_point, **kwargs):
|
||||||
|
ApplyMethod.__init__(self, car.move_to, target_point, **kwargs)
|
||||||
|
displacement = self.ending_mobject.get_right()-self.starting_mobject.get_right()
|
||||||
|
distance = np.linalg.norm(displacement)
|
||||||
|
tire_radius = car.get_tires()[0].get_width()/2
|
||||||
|
self.total_tire_radians = -distance/tire_radius
|
||||||
|
|
||||||
|
def update_mobject(self, alpha):
|
||||||
|
ApplyMethod.update_mobject(self, alpha)
|
||||||
|
if alpha == 0:
|
||||||
|
return
|
||||||
|
radians = alpha*self.total_tire_radians
|
||||||
|
for tire in self.mobject.get_tires():
|
||||||
|
tire.rotate_in_place(radians)
|
||||||
|
|
||||||
|
class IncrementNumber(Succession):
|
||||||
|
CONFIG = {
|
||||||
|
"start_num" : 0,
|
||||||
|
"changes_per_second" : 1,
|
||||||
|
"run_time" : 11,
|
||||||
|
}
|
||||||
|
def __init__(self, num_mob, **kwargs):
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
n_iterations = int(self.run_time * self.changes_per_second)
|
||||||
|
new_num_mobs = [
|
||||||
|
TexMobject(str(num)).move_to(num_mob, LEFT)
|
||||||
|
for num in range(self.start_num, self.start_num+n_iterations)
|
||||||
|
]
|
||||||
|
transforms = [
|
||||||
|
Transform(
|
||||||
|
num_mob, new_num_mob,
|
||||||
|
run_time = 1.0/self.changes_per_second,
|
||||||
|
rate_func = squish_rate_func(smooth, 0, 0.5)
|
||||||
|
)
|
||||||
|
for new_num_mob in new_num_mobs
|
||||||
|
]
|
||||||
|
Succession.__init__(
|
||||||
|
self, *transforms, **{
|
||||||
|
"rate_func" : None,
|
||||||
|
"run_time" : self.run_time,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
class IncrementTest(Scene):
|
||||||
|
def construct(self):
|
||||||
|
num = TexMobject("0")
|
||||||
|
num.shift(UP)
|
||||||
|
self.play(IncrementNumber(num))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
|
||||||
|
class Chapter2OpeningQuote(OpeningQuote):
|
||||||
|
CONFIG = {
|
||||||
|
"quote" : [
|
||||||
|
"So far as the theories of mathematics are about",
|
||||||
|
"reality,",
|
||||||
|
"they are not",
|
||||||
|
"certain;",
|
||||||
|
"so far as they are",
|
||||||
|
"certain,",
|
||||||
|
"they are not about",
|
||||||
|
"reality.",
|
||||||
|
],
|
||||||
|
"highlighted_quote_terms" : {
|
||||||
|
"reality," : BLUE,
|
||||||
|
"certain;" : GREEN,
|
||||||
|
"certain," : GREEN,
|
||||||
|
"reality." : BLUE,
|
||||||
|
},
|
||||||
|
"author" : "Albert Einstein"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Introduction(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
self.student_says(
|
||||||
|
"What is a derivative?"
|
||||||
|
)
|
||||||
|
self.play(self.get_teacher().change_mode, "happy")
|
||||||
|
self.dither()
|
||||||
|
self.teacher_says(
|
||||||
|
"It's actually a \\\\",
|
||||||
|
"very subtle idea",
|
||||||
|
target_mode = "well"
|
||||||
|
)
|
||||||
|
self.change_student_modes(None, "pondering", "thinking")
|
||||||
|
self.dither()
|
||||||
|
self.change_student_modes("erm")
|
||||||
|
self.student_says(
|
||||||
|
"Doesn't the derivative measure\\\\",
|
||||||
|
"instantaneous rate of change", "?",
|
||||||
|
student_index = 0,
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
bubble = self.get_students()[0].bubble
|
||||||
|
phrase = bubble.content[1]
|
||||||
|
bubble.content.remove(phrase)
|
||||||
|
self.play(
|
||||||
|
phrase.center,
|
||||||
|
phrase.scale, 1.5,
|
||||||
|
phrase.to_edge, UP,
|
||||||
|
FadeOut(bubble),
|
||||||
|
FadeOut(bubble.content),
|
||||||
|
*it.chain(*[
|
||||||
|
[
|
||||||
|
pi.change_mode, mode,
|
||||||
|
pi.look_at, SPACE_HEIGHT*UP
|
||||||
|
]
|
||||||
|
for pi, mode in zip(self.get_everyone(), [
|
||||||
|
"speaking", "pondering", "confused", "confused",
|
||||||
|
])
|
||||||
|
])
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
change = VGroup(*phrase[-len("change"):])
|
||||||
|
instantaneous = VGroup(*phrase[:len("instantaneous")])
|
||||||
|
change_brace = Brace(change)
|
||||||
|
change_description = change_brace.get_text(
|
||||||
|
"Requires multiple \\\\ points in time"
|
||||||
|
)
|
||||||
|
instantaneous_brace = Brace(instantaneous)
|
||||||
|
instantaneous_description = instantaneous_brace.get_text(
|
||||||
|
"One point \\\\ in time"
|
||||||
|
)
|
||||||
|
clock = Clock()
|
||||||
|
clock.next_to(change_description, DOWN)
|
||||||
|
def get_clock_anim(run_time = 3):
|
||||||
|
return ClockPassesTime(
|
||||||
|
clock,
|
||||||
|
hours_passed = 0.4*run_time,
|
||||||
|
run_time = run_time,
|
||||||
|
)
|
||||||
|
self.play(FadeIn(clock))
|
||||||
|
self.play(
|
||||||
|
change.gradient_highlight, BLUE, YELLOW,
|
||||||
|
GrowFromCenter(change_brace),
|
||||||
|
Write(change_description),
|
||||||
|
get_clock_anim()
|
||||||
|
)
|
||||||
|
self.play(get_clock_anim(1))
|
||||||
|
stopped_clock = clock.copy()
|
||||||
|
stopped_clock.next_to(instantaneous_description, DOWN)
|
||||||
|
self.play(
|
||||||
|
instantaneous.highlight, BLUE,
|
||||||
|
GrowFromCenter(instantaneous_brace),
|
||||||
|
Transform(change_description.copy(), instantaneous_description),
|
||||||
|
clock.copy().next_to, instantaneous_description, DOWN,
|
||||||
|
get_clock_anim(3)
|
||||||
|
)
|
||||||
|
self.play(get_clock_anim(6))
|
||||||
|
|
||||||
|
class FathersOfCalculus(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"names" : [
|
||||||
|
"Barrow",
|
||||||
|
"Newton",
|
||||||
|
"Leibniz",
|
||||||
|
"Cauchy",
|
||||||
|
"Weierstrass",
|
||||||
|
],
|
||||||
|
"picture_height" : 2.5,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
title = TextMobject("(A few) Fathers of Calculus")
|
||||||
|
title.to_edge(UP)
|
||||||
|
self.add(title)
|
||||||
|
|
||||||
|
men = Mobject()
|
||||||
|
for name in self.names:
|
||||||
|
image = ImageMobject(name, invert = False)
|
||||||
|
image.scale_to_fit_height(self.picture_height)
|
||||||
|
title = TextMobject(name)
|
||||||
|
title.scale(0.8)
|
||||||
|
title.next_to(image, DOWN)
|
||||||
|
image.add(title)
|
||||||
|
men.add(image)
|
||||||
|
men.arrange_submobjects(RIGHT, aligned_edge = UP)
|
||||||
|
men.shift(DOWN)
|
||||||
|
|
||||||
|
discover_brace = Brace(Mobject(*men[:3]), UP)
|
||||||
|
discover = discover_brace.get_text("Discovered it")
|
||||||
|
VGroup(discover_brace, discover).highlight(BLUE)
|
||||||
|
rigor_brace = Brace(Mobject(*men[3:]), UP)
|
||||||
|
rigor = rigor_brace.get_text("Made it rigorous")
|
||||||
|
rigor.shift(0.1*DOWN)
|
||||||
|
VGroup(rigor_brace, rigor).highlight(YELLOW)
|
||||||
|
|
||||||
|
|
||||||
|
for man in men:
|
||||||
|
self.play(FadeIn(man))
|
||||||
|
self.play(
|
||||||
|
GrowFromCenter(discover_brace),
|
||||||
|
Write(discover, run_time = 1)
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
GrowFromCenter(rigor_brace),
|
||||||
|
Write(rigor, run_time = 1)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class IntroduceCar(Scene):
|
||||||
|
def construct(self):
|
||||||
|
point_A = DOWN+4*LEFT
|
||||||
|
point_B = DOWN+5*RIGHT
|
||||||
|
A = Dot(point_A)
|
||||||
|
B = Dot(point_B)
|
||||||
|
line = Line(point_A, point_B)
|
||||||
|
VGroup(A, B, line).highlight(WHITE)
|
||||||
|
for dot, tex in (A, "A"), (B, "B"):
|
||||||
|
label = TexMobject(tex).next_to(dot, DOWN)
|
||||||
|
dot.add(label)
|
||||||
|
|
||||||
|
car = Car()
|
||||||
|
car.move_to(point_A)
|
||||||
|
front_line = car.get_front_line()
|
||||||
|
|
||||||
|
time_label = TextMobject("Time (in seconds):", "0")
|
||||||
|
time_label.shift(2*UP)
|
||||||
|
|
||||||
|
distance_brace = Brace(line, UP)
|
||||||
|
# distance_brace.set_fill(opacity = 0.5)
|
||||||
|
distance = distance_brace.get_text("100m")
|
||||||
|
|
||||||
|
self.add(A, B, line, car, time_label)
|
||||||
|
self.play(ShowCreation(front_line))
|
||||||
|
self.play(FadeOut(front_line))
|
||||||
|
self.play(
|
||||||
|
MoveCar(car, point_B, run_time = 10),
|
||||||
|
IncrementNumber(time_label[1], run_time = 11)
|
||||||
|
)
|
||||||
|
front_line = car.get_front_line()
|
||||||
|
self.play(ShowCreation(front_line))
|
||||||
|
self.play(FadeOut(front_line))
|
||||||
|
self.play(
|
||||||
|
GrowFromCenter(distance_brace),
|
||||||
|
Write(distance)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(
|
||||||
|
car.move_to, point_A,
|
||||||
|
FadeOut(time_label),
|
||||||
|
FadeOut(distance_brace),
|
||||||
|
FadeOut(distance)
|
||||||
|
)
|
||||||
|
graph_scene = GraphCarTrajectory(skip_animations = True)
|
||||||
|
origin = graph_scene.graph_origin
|
||||||
|
top = graph_scene.coords_to_point(0, 100)
|
||||||
|
new_length = np.linalg.norm(top-origin)
|
||||||
|
new_point_B = point_A + new_length*RIGHT
|
||||||
|
|
||||||
|
group = VGroup(car, A, B, line)
|
||||||
|
for mob in group:
|
||||||
|
mob.generate_target()
|
||||||
|
group.target = VGroup(*[m.target for m in group])
|
||||||
|
B.target.shift(new_point_B - point_B)
|
||||||
|
line.target.put_start_and_end_on(
|
||||||
|
point_A, new_point_B
|
||||||
|
)
|
||||||
|
|
||||||
|
group.target.rotate(np.pi/2, about_point = point_A)
|
||||||
|
group.target.shift(graph_scene.graph_origin - point_A)
|
||||||
|
self.play(MoveToTarget(group))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class GraphCarTrajectory(GraphScene):
|
||||||
|
CONFIG = {
|
||||||
|
"x_min" : 0,
|
||||||
|
"x_axis_label" : "Time (seconds)",
|
||||||
|
"y_min" : 0,
|
||||||
|
"y_max" : 110,
|
||||||
|
"y_tick_frequency" : 10,
|
||||||
|
"y_labeled_nums" : range(10, 110, 10),
|
||||||
|
"y_axis_label" : "Distance traveled \\\\ (meters)",
|
||||||
|
"graph_origin" : 2.5*DOWN + 5*LEFT,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.setup_axes(animate = False)
|
||||||
|
graph = self.graph_function(
|
||||||
|
lambda t : 100*smooth(t/10.),
|
||||||
|
)
|
||||||
|
self.remove(graph)
|
||||||
|
|
||||||
|
car = Car()
|
||||||
|
car.rotate(np.pi/2)
|
||||||
|
car.move_to(self.coords_to_point(0, 0))
|
||||||
|
self.add(car)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from helpers import *
|
||||||
|
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
# from topics.geometry import
|
# from topics.geometry import
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject, TextMobject
|
||||||
from mobject.vectorized_mobject import VGroup, VectorizedPoint
|
from mobject.vectorized_mobject import VGroup, VectorizedPoint
|
||||||
from animation.simple_animations import Write, ShowCreation
|
from animation.simple_animations import Write, ShowCreation
|
||||||
from topics.number_line import NumberLine
|
from topics.number_line import NumberLine
|
||||||
|
@ -15,14 +15,14 @@ class GraphScene(Scene):
|
||||||
"x_max" : 10,
|
"x_max" : 10,
|
||||||
"x_axis_width" : 9,
|
"x_axis_width" : 9,
|
||||||
"x_tick_frequency" : 1,
|
"x_tick_frequency" : 1,
|
||||||
"x_leftmost_tick" : -1,
|
"x_leftmost_tick" : None, #Change if different from x_min
|
||||||
"x_labeled_nums" : range(1, 10),
|
"x_labeled_nums" : range(1, 10),
|
||||||
"x_axis_label" : "x",
|
"x_axis_label" : "x",
|
||||||
"y_min" : -1,
|
"y_min" : -1,
|
||||||
"y_max" : 10,
|
"y_max" : 10,
|
||||||
"y_axis_height" : 6,
|
"y_axis_height" : 6,
|
||||||
"y_tick_frequency" : 1,
|
"y_tick_frequency" : 1,
|
||||||
"y_bottom_tick" : -1,
|
"y_bottom_tick" : None, #Change if different from y_min
|
||||||
"y_labeled_nums" : range(1, 10),
|
"y_labeled_nums" : range(1, 10),
|
||||||
"y_axis_label" : "y",
|
"y_axis_label" : "y",
|
||||||
"axes_color" : GREY,
|
"axes_color" : GREY,
|
||||||
|
@ -36,15 +36,16 @@ class GraphScene(Scene):
|
||||||
x_max = self.x_max,
|
x_max = self.x_max,
|
||||||
space_unit_to_num = self.x_axis_width/x_num_range,
|
space_unit_to_num = self.x_axis_width/x_num_range,
|
||||||
tick_frequency = self.x_tick_frequency,
|
tick_frequency = self.x_tick_frequency,
|
||||||
leftmost_tick = self.x_leftmost_tick,
|
leftmost_tick = self.x_leftmost_tick or self.x_min,
|
||||||
numbers_with_elongated_ticks = self.x_labeled_nums,
|
numbers_with_elongated_ticks = self.x_labeled_nums,
|
||||||
color = self.axes_color
|
color = self.axes_color
|
||||||
)
|
)
|
||||||
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
||||||
if self.x_labeled_nums:
|
if self.x_labeled_nums:
|
||||||
x_axis.add_numbers(*self.x_labeled_nums)
|
x_axis.add_numbers(*self.x_labeled_nums)
|
||||||
x_label = TexMobject(self.x_axis_label)
|
x_label = TextMobject(self.x_axis_label)
|
||||||
x_label.next_to(x_axis, RIGHT+UP, buff = SMALL_BUFF)
|
x_label.next_to(x_axis, RIGHT+UP, buff = SMALL_BUFF)
|
||||||
|
x_label.shift_onto_screen()
|
||||||
x_axis.add(x_label)
|
x_axis.add(x_label)
|
||||||
self.x_axis_label_mob = x_label
|
self.x_axis_label_mob = x_label
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ class GraphScene(Scene):
|
||||||
x_max = self.y_max,
|
x_max = self.y_max,
|
||||||
space_unit_to_num = self.y_axis_height/y_num_range,
|
space_unit_to_num = self.y_axis_height/y_num_range,
|
||||||
tick_frequency = self.y_tick_frequency,
|
tick_frequency = self.y_tick_frequency,
|
||||||
leftmost_tick = self.y_bottom_tick,
|
leftmost_tick = self.y_bottom_tick or self.y_min,
|
||||||
numbers_with_elongated_ticks = self.y_labeled_nums,
|
numbers_with_elongated_ticks = self.y_labeled_nums,
|
||||||
color = self.axes_color
|
color = self.axes_color
|
||||||
)
|
)
|
||||||
|
@ -63,8 +64,9 @@ class GraphScene(Scene):
|
||||||
if self.y_labeled_nums:
|
if self.y_labeled_nums:
|
||||||
y_axis.add_numbers(*self.y_labeled_nums)
|
y_axis.add_numbers(*self.y_labeled_nums)
|
||||||
y_axis.numbers.shift(self.y_axis_numbers_nudge)
|
y_axis.numbers.shift(self.y_axis_numbers_nudge)
|
||||||
y_label = TexMobject(self.y_axis_label)
|
y_label = TextMobject(self.y_axis_label)
|
||||||
y_label.next_to(y_axis.get_top(), RIGHT, buff = 2*MED_BUFF)
|
y_label.next_to(y_axis.get_top(), RIGHT, buff = 2*MED_BUFF)
|
||||||
|
y_label.shift_onto_screen()
|
||||||
y_axis.add(y_label)
|
y_axis.add(y_label)
|
||||||
self.y_axis_label_mob = y_label
|
self.y_axis_label_mob = y_label
|
||||||
|
|
||||||
|
@ -82,7 +84,7 @@ class GraphScene(Scene):
|
||||||
|
|
||||||
def graph_function(self, func,
|
def graph_function(self, func,
|
||||||
color = BLUE,
|
color = BLUE,
|
||||||
animate = True,
|
animate = False,
|
||||||
is_main_graph = True,
|
is_main_graph = True,
|
||||||
):
|
):
|
||||||
|
|
||||||
|
|
|
@ -403,7 +403,8 @@ class Mobject(object):
|
||||||
|
|
||||||
def reduce_across_dimension(self, points_func, reduce_func, dim):
|
def reduce_across_dimension(self, points_func, reduce_func, dim):
|
||||||
try:
|
try:
|
||||||
values = [points_func(self.points[:, dim])]
|
points = self.get_points_defining_boundary()
|
||||||
|
values = [points_func(points[:, dim])]
|
||||||
except:
|
except:
|
||||||
values = []
|
values = []
|
||||||
values += [
|
values += [
|
||||||
|
@ -426,6 +427,9 @@ class Mobject(object):
|
||||||
|
|
||||||
### Getters ###
|
### Getters ###
|
||||||
|
|
||||||
|
def get_points_defining_boundary(self):
|
||||||
|
return self.points
|
||||||
|
|
||||||
def get_num_points(self):
|
def get_num_points(self):
|
||||||
return len(self.points)
|
return len(self.points)
|
||||||
|
|
||||||
|
@ -517,7 +521,7 @@ class Mobject(object):
|
||||||
|
|
||||||
def submobject_family(self):
|
def submobject_family(self):
|
||||||
sub_families = map(Mobject.submobject_family, self.submobjects)
|
sub_families = map(Mobject.submobject_family, self.submobjects)
|
||||||
all_mobjects = [self] + reduce(op.add, sub_families, [])
|
all_mobjects = [self] + list(it.chain(*sub_families))
|
||||||
return remove_list_redundancies(all_mobjects)
|
return remove_list_redundancies(all_mobjects)
|
||||||
|
|
||||||
def family_members_with_points(self):
|
def family_members_with_points(self):
|
||||||
|
|
|
@ -204,7 +204,8 @@ class VMobject(Mobject):
|
||||||
it comes time to display.
|
it comes time to display.
|
||||||
"""
|
"""
|
||||||
subpath_mobject = self.copy()#TODO, better way?
|
subpath_mobject = self.copy()#TODO, better way?
|
||||||
# subpath_mobject = VMobject()
|
subpath_mobject.submobjects = []
|
||||||
|
# subpath_mobject = self.__class__()
|
||||||
subpath_mobject.is_subpath = True
|
subpath_mobject.is_subpath = True
|
||||||
subpath_mobject.set_points(points)
|
subpath_mobject.set_points(points)
|
||||||
self.add(subpath_mobject)
|
self.add(subpath_mobject)
|
||||||
|
@ -253,6 +254,13 @@ class VMobject(Mobject):
|
||||||
for i in range(3)
|
for i in range(3)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_anchors(self):
|
||||||
|
return self.points[::3]
|
||||||
|
|
||||||
|
def get_points_defining_boundary(self):
|
||||||
|
return self.get_anchors()
|
||||||
|
|
||||||
|
|
||||||
## Alignment
|
## Alignment
|
||||||
|
|
||||||
def align_points(self, mobject):
|
def align_points(self, mobject):
|
||||||
|
|
|
@ -17,67 +17,13 @@ from topics.number_line import *
|
||||||
from topics.combinatorics import *
|
from topics.combinatorics import *
|
||||||
from topics.numerals import *
|
from topics.numerals import *
|
||||||
from topics.three_dimensions import *
|
from topics.three_dimensions import *
|
||||||
|
from topics.objects import *
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
from camera import Camera
|
from camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
|
|
||||||
|
|
||||||
class Clock(VMobject):
|
|
||||||
CONFIG = {
|
|
||||||
"propogate_style_to_family" : True,
|
|
||||||
}
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
circle = Circle()
|
|
||||||
ticks = []
|
|
||||||
for x in range(12):
|
|
||||||
alpha = x/12.
|
|
||||||
point = complex_to_R3(
|
|
||||||
np.exp(2*np.pi*alpha*complex(0, 1))
|
|
||||||
)
|
|
||||||
length = 0.2 if x%3 == 0 else 0.1
|
|
||||||
ticks.append(
|
|
||||||
Line(point, (1-length)*point)
|
|
||||||
)
|
|
||||||
self.hour_hand = Line(ORIGIN, 0.3*UP)
|
|
||||||
self.minute_hand = Line(ORIGIN, 0.6*UP)
|
|
||||||
for hand in self.hour_hand, self.minute_hand:
|
|
||||||
#Balance out where the center is
|
|
||||||
hand.add(VectorizedPoint(-hand.get_end()))
|
|
||||||
|
|
||||||
VMobject.__init__(
|
|
||||||
self, circle,
|
|
||||||
self.hour_hand, self.minute_hand,
|
|
||||||
*ticks
|
|
||||||
)
|
|
||||||
|
|
||||||
class ClockPassesTime(Animation):
|
|
||||||
CONFIG = {
|
|
||||||
"run_time" : 5,
|
|
||||||
"rate_func" : None,
|
|
||||||
}
|
|
||||||
def __init__(self, clock, **kwargs):
|
|
||||||
assert(isinstance(clock, Clock))
|
|
||||||
rot_kwargs = {
|
|
||||||
"axis" : OUT,
|
|
||||||
"in_place" : True
|
|
||||||
}
|
|
||||||
self.hour_rotation = Rotating(
|
|
||||||
clock.hour_hand,
|
|
||||||
radians = -2*np.pi,
|
|
||||||
**rot_kwargs
|
|
||||||
)
|
|
||||||
self.minute_rotation = Rotating(
|
|
||||||
clock.minute_hand,
|
|
||||||
radians = -12*2*np.pi,
|
|
||||||
**rot_kwargs
|
|
||||||
)
|
|
||||||
Animation.__init__(self, clock, **kwargs)
|
|
||||||
|
|
||||||
def update_mobject(self, alpha):
|
|
||||||
for rotation in self.hour_rotation, self.minute_rotation:
|
|
||||||
rotation.update_mobject(alpha)
|
|
||||||
|
|
||||||
class SideGigToFullTime(Scene):
|
class SideGigToFullTime(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
morty = Mortimer()
|
morty = Mortimer()
|
||||||
|
|
|
@ -1357,7 +1357,8 @@ class FromRealToComplex(ComplexTransformationScene):
|
||||||
VGroup(*lines[1::2]).highlight(RED)
|
VGroup(*lines[1::2]).highlight(RED)
|
||||||
|
|
||||||
final_dot = Dot(
|
final_dot = Dot(
|
||||||
self.z_to_point(power_sums[-1]),
|
# self.z_to_point(power_sums[-1]),
|
||||||
|
self.z_to_point(zeta(exponent)),
|
||||||
color = self.output_color
|
color = self.output_color
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3448,7 +3449,7 @@ class Thumbnail(ZetaTransformationScene):
|
||||||
self.apply_zeta_function()
|
self.apply_zeta_function()
|
||||||
self.plane.set_stroke(width = 4)
|
self.plane.set_stroke(width = 4)
|
||||||
|
|
||||||
div_sum = TexMobject("1+2+3+4+\\cdots = -", "\\frac{1}{12}")
|
div_sum = TexMobject("-\\frac{1}{12} = ", "1+2+3+4+\\cdots")
|
||||||
div_sum.scale_to_fit_width(2*SPACE_WIDTH-1)
|
div_sum.scale_to_fit_width(2*SPACE_WIDTH-1)
|
||||||
div_sum.to_edge(DOWN)
|
div_sum.to_edge(DOWN)
|
||||||
div_sum.highlight(YELLOW)
|
div_sum.highlight(YELLOW)
|
||||||
|
@ -3460,7 +3461,7 @@ class Thumbnail(ZetaTransformationScene):
|
||||||
zeta.to_corner(UP+LEFT)
|
zeta.to_corner(UP+LEFT)
|
||||||
|
|
||||||
million = TexMobject("\\$1{,}000{,}000")
|
million = TexMobject("\\$1{,}000{,}000")
|
||||||
million.scale_to_fit_width(SPACE_WIDTH)
|
million.scale_to_fit_width(SPACE_WIDTH+1)
|
||||||
million.to_edge(UP+RIGHT)
|
million.to_edge(UP+RIGHT)
|
||||||
million.highlight(GREEN_B)
|
million.highlight(GREEN_B)
|
||||||
million.add_background_rectangle()
|
million.add_background_rectangle()
|
||||||
|
@ -3468,9 +3469,51 @@ class Thumbnail(ZetaTransformationScene):
|
||||||
self.add(div_sum, million, zeta)
|
self.add(div_sum, million, zeta)
|
||||||
|
|
||||||
|
|
||||||
|
class ZetaPartialSums(ZetaTransformationScene):
|
||||||
|
CONFIG = {
|
||||||
|
"anchor_density" : 35,
|
||||||
|
"num_partial_sums" : 12,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.add_transformable_plane()
|
||||||
|
self.add_extra_plane_lines_for_zeta()
|
||||||
|
self.prepare_for_transformation(self.plane)
|
||||||
|
|
||||||
|
N_list = [2**k for k in range(self.num_partial_sums)]
|
||||||
|
sigma = TexMobject(
|
||||||
|
"\\sum_{n = 1}^N \\frac{1}{n^s}"
|
||||||
|
)
|
||||||
|
sigmas = []
|
||||||
|
for N in N_list + ["\\infty"]:
|
||||||
|
tex = TexMobject(str(N))
|
||||||
|
tex.highlight(YELLOW)
|
||||||
|
new_sigma = sigma.copy()
|
||||||
|
top = new_sigma[0]
|
||||||
|
tex.move_to(top, DOWN)
|
||||||
|
new_sigma.remove(top)
|
||||||
|
new_sigma.add(tex)
|
||||||
|
new_sigma.to_corner(UP+LEFT)
|
||||||
|
sigmas.append(new_sigma)
|
||||||
|
|
||||||
|
def get_partial_sum_func(n_terms):
|
||||||
|
return lambda s : sum([1./(n**s) for n in range(1, n_terms+1)])
|
||||||
|
interim_planes = [
|
||||||
|
self.plane.copy().apply_complex_function(
|
||||||
|
get_partial_sum_func(N)
|
||||||
|
)
|
||||||
|
for N in N_list
|
||||||
|
]
|
||||||
|
interim_planes.append(self.plane.copy().apply_complex_function(zeta))
|
||||||
|
symbol = VGroup(TexMobject("s"))
|
||||||
|
symbol.scale(2)
|
||||||
|
symbol.highlight(YELLOW)
|
||||||
|
symbol.to_corner(UP+LEFT)
|
||||||
|
for plane, sigma in zip(interim_planes, sigmas):
|
||||||
|
self.play(
|
||||||
|
Transform(self.plane, plane),
|
||||||
|
Transform(symbol, sigma)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ class Scene(object):
|
||||||
if animation.run_time != max_run_time:
|
if animation.run_time != max_run_time:
|
||||||
new_rate_func = squish_rate_func(
|
new_rate_func = squish_rate_func(
|
||||||
animation.get_rate_func(),
|
animation.get_rate_func(),
|
||||||
0, 1./max_run_time
|
0, float(animation.run_time)/max_run_time
|
||||||
)
|
)
|
||||||
animation.set_rate_func(new_rate_func)
|
animation.set_rate_func(new_rate_func)
|
||||||
animation.set_run_time(max_run_time)
|
animation.set_run_time(max_run_time)
|
||||||
|
@ -337,6 +337,7 @@ class Scene(object):
|
||||||
full_path = os.path.join(path, file_name)
|
full_path = os.path.join(path, file_name)
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
|
self.update_frame()
|
||||||
Image.fromarray(self.get_frame()).save(full_path)
|
Image.fromarray(self.get_frame()).save(full_path)
|
||||||
|
|
||||||
def get_movie_file_path(self, name, extension):
|
def get_movie_file_path(self, name, extension):
|
||||||
|
|
|
@ -416,11 +416,15 @@ class TeacherStudentsScene(Scene):
|
||||||
for x in range(num_times):
|
for x in range(num_times):
|
||||||
pi_creature = random.choice(self.get_everyone())
|
pi_creature = random.choice(self.get_everyone())
|
||||||
self.play(Blink(pi_creature))
|
self.play(Blink(pi_creature))
|
||||||
self.dither()
|
Scene.dither(self)
|
||||||
|
|
||||||
|
def dither(self, time = 1):
|
||||||
|
self.random_blink(num_times = max(time/2, 1))
|
||||||
|
|
||||||
def change_student_modes(self, *modes, **kwargs):
|
def change_student_modes(self, *modes, **kwargs):
|
||||||
added_anims = kwargs.get("added_anims", [])
|
added_anims = kwargs.get("added_anims", [])
|
||||||
pairs = zip(self.get_students(), modes)
|
pairs = zip(self.get_students(), modes)
|
||||||
|
pairs = [(s, m) for s, m in pairs if m is not None]
|
||||||
start = VGroup(*[s for s, m in pairs])
|
start = VGroup(*[s for s, m in pairs])
|
||||||
target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
|
target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
|
||||||
self.play(
|
self.play(
|
||||||
|
|
|
@ -5,6 +5,10 @@ from mobject.vectorized_mobject import VGroup
|
||||||
from mobject.svg_mobject import SVGMobject
|
from mobject.svg_mobject import SVGMobject
|
||||||
from mobject.tex_mobject import TextMobject
|
from mobject.tex_mobject import TextMobject
|
||||||
|
|
||||||
|
from animation import Animation
|
||||||
|
from animation.simple_animations import Rotating
|
||||||
|
|
||||||
|
from topics.geometry import Circle, Line
|
||||||
|
|
||||||
|
|
||||||
class VideoIcon(SVGMobject):
|
class VideoIcon(SVGMobject):
|
||||||
|
@ -50,6 +54,63 @@ class Headphones(SVGMobject):
|
||||||
self.set_stroke(width = 0)
|
self.set_stroke(width = 0)
|
||||||
self.set_fill(color = self.color)
|
self.set_fill(color = self.color)
|
||||||
|
|
||||||
|
class Clock(VGroup):
|
||||||
|
CONFIG = {
|
||||||
|
"propogate_style_to_family" : True,
|
||||||
|
}
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
circle = Circle()
|
||||||
|
ticks = []
|
||||||
|
for x in range(12):
|
||||||
|
alpha = x/12.
|
||||||
|
point = complex_to_R3(
|
||||||
|
np.exp(2*np.pi*alpha*complex(0, 1))
|
||||||
|
)
|
||||||
|
length = 0.2 if x%3 == 0 else 0.1
|
||||||
|
ticks.append(
|
||||||
|
Line(point, (1-length)*point)
|
||||||
|
)
|
||||||
|
self.hour_hand = Line(ORIGIN, 0.3*UP)
|
||||||
|
self.minute_hand = Line(ORIGIN, 0.6*UP)
|
||||||
|
# for hand in self.hour_hand, self.minute_hand:
|
||||||
|
# #Balance out where the center is
|
||||||
|
# hand.add(VectorizedPoint(-hand.get_end()))
|
||||||
|
|
||||||
|
VGroup.__init__(
|
||||||
|
self, circle,
|
||||||
|
self.hour_hand, self.minute_hand,
|
||||||
|
*ticks
|
||||||
|
)
|
||||||
|
|
||||||
|
class ClockPassesTime(Animation):
|
||||||
|
CONFIG = {
|
||||||
|
"run_time" : 5,
|
||||||
|
"hours_passed" : 12,
|
||||||
|
"rate_func" : None,
|
||||||
|
}
|
||||||
|
def __init__(self, clock, **kwargs):
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
assert(isinstance(clock, Clock))
|
||||||
|
rot_kwargs = {
|
||||||
|
"axis" : OUT,
|
||||||
|
"about_point" : clock.get_center()
|
||||||
|
}
|
||||||
|
hour_radians = -self.hours_passed*2*np.pi/12
|
||||||
|
self.hour_rotation = Rotating(
|
||||||
|
clock.hour_hand,
|
||||||
|
radians = hour_radians,
|
||||||
|
**rot_kwargs
|
||||||
|
)
|
||||||
|
self.minute_rotation = Rotating(
|
||||||
|
clock.minute_hand,
|
||||||
|
radians = 12*hour_radians,
|
||||||
|
**rot_kwargs
|
||||||
|
)
|
||||||
|
Animation.__init__(self, clock, **kwargs)
|
||||||
|
|
||||||
|
def update_mobject(self, alpha):
|
||||||
|
for rotation in self.hour_rotation, self.minute_rotation:
|
||||||
|
rotation.update_mobject(alpha)
|
||||||
|
|
||||||
class Bubble(SVGMobject):
|
class Bubble(SVGMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
Loading…
Add table
Reference in a new issue