mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +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
|
||||
ka_playgrounds/
|
||||
playground.py
|
||||
special_animations.py
|
||||
prettiness_hall_of_fame.py
|
||||
files/
|
|
@ -11,24 +11,30 @@ from animation import Animation
|
|||
|
||||
class Rotating(Animation):
|
||||
CONFIG = {
|
||||
"axes" : [RIGHT, UP],
|
||||
"axis" : None,
|
||||
"axes" : [],
|
||||
"axis" : OUT,
|
||||
"radians" : 2*np.pi,
|
||||
"run_time" : 20.0,
|
||||
"run_time" : 5,
|
||||
"rate_func" : None,
|
||||
"in_place" : True,
|
||||
"about_point" : None,
|
||||
}
|
||||
def update_submobject(self, submobject, starting_submobject, alpha):
|
||||
submobject.points = np.array(starting_submobject.points)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
Animation.update_mobject(self, alpha)
|
||||
axes = [self.axis] if self.axis is not None else self.axes
|
||||
if self.in_place:
|
||||
method = self.mobject.rotate_in_place
|
||||
else:
|
||||
method = self.mobject.rotate
|
||||
method(alpha*self.radians, axes = axes)
|
||||
axes = self.axes if self.axes else [self.axis]
|
||||
about_point = None
|
||||
if self.about_point is not None:
|
||||
about_point = self.about_point
|
||||
elif self.in_place: #This is superseeded
|
||||
self.about_point = self.mobject.get_center()
|
||||
self.mobject.rotate(
|
||||
alpha*self.radians,
|
||||
axes = axes,
|
||||
about_point = self.about_point
|
||||
)
|
||||
|
||||
|
||||
class ShowPartial(Animation):
|
||||
|
@ -267,8 +273,14 @@ class Succession(Animation):
|
|||
last_anim = self.anims[self.last_index]
|
||||
last_anim.clean_up()
|
||||
if last_anim.mobject is curr_anim.mobject:
|
||||
self.anims[index].starting_mobject = curr_anim.mobject.copy()
|
||||
self.anims[index].update_mobject(scaled_alpha - index)
|
||||
#TODO, is there a way to do this that doesn't
|
||||
#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
|
||||
|
||||
|
||||
|
|
|
@ -253,20 +253,23 @@ class OpeningQuote(Scene):
|
|||
self.play(Write(author, run_time = 3))
|
||||
self.dither()
|
||||
|
||||
def get_quote(self):
|
||||
def get_quote(self, max_width = 2*SPACE_WIDTH-1):
|
||||
if isinstance(self.quote, str):
|
||||
quote = TextMobject(
|
||||
"``%s''"%self.quote.strip(),
|
||||
alignment = "",
|
||||
)
|
||||
else:
|
||||
words = list(self.quote)
|
||||
words[0] = "``%s"%words[0]
|
||||
words[-1] += "''"
|
||||
words = ["``"] + list(self.quote) + ["''"]
|
||||
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():
|
||||
quote.highlight_by_tex(term, color)
|
||||
quote.to_edge(UP)
|
||||
if quote.get_width() > max_width:
|
||||
quote.scale_to_fit_width(max_width)
|
||||
return 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 topics.geometry import
|
||||
from mobject.tex_mobject import TexMobject
|
||||
from mobject.tex_mobject import TexMobject, TextMobject
|
||||
from mobject.vectorized_mobject import VGroup, VectorizedPoint
|
||||
from animation.simple_animations import Write, ShowCreation
|
||||
from topics.number_line import NumberLine
|
||||
|
@ -15,14 +15,14 @@ class GraphScene(Scene):
|
|||
"x_max" : 10,
|
||||
"x_axis_width" : 9,
|
||||
"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_axis_label" : "x",
|
||||
"y_min" : -1,
|
||||
"y_max" : 10,
|
||||
"y_axis_height" : 6,
|
||||
"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_axis_label" : "y",
|
||||
"axes_color" : GREY,
|
||||
|
@ -36,15 +36,16 @@ class GraphScene(Scene):
|
|||
x_max = self.x_max,
|
||||
space_unit_to_num = self.x_axis_width/x_num_range,
|
||||
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,
|
||||
color = self.axes_color
|
||||
)
|
||||
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
||||
if 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.shift_onto_screen()
|
||||
x_axis.add(x_label)
|
||||
self.x_axis_label_mob = x_label
|
||||
|
||||
|
@ -54,7 +55,7 @@ class GraphScene(Scene):
|
|||
x_max = self.y_max,
|
||||
space_unit_to_num = self.y_axis_height/y_num_range,
|
||||
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,
|
||||
color = self.axes_color
|
||||
)
|
||||
|
@ -63,8 +64,9 @@ class GraphScene(Scene):
|
|||
if self.y_labeled_nums:
|
||||
y_axis.add_numbers(*self.y_labeled_nums)
|
||||
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.shift_onto_screen()
|
||||
y_axis.add(y_label)
|
||||
self.y_axis_label_mob = y_label
|
||||
|
||||
|
@ -82,7 +84,7 @@ class GraphScene(Scene):
|
|||
|
||||
def graph_function(self, func,
|
||||
color = BLUE,
|
||||
animate = True,
|
||||
animate = False,
|
||||
is_main_graph = True,
|
||||
):
|
||||
|
||||
|
|
|
@ -403,7 +403,8 @@ class Mobject(object):
|
|||
|
||||
def reduce_across_dimension(self, points_func, reduce_func, dim):
|
||||
try:
|
||||
values = [points_func(self.points[:, dim])]
|
||||
points = self.get_points_defining_boundary()
|
||||
values = [points_func(points[:, dim])]
|
||||
except:
|
||||
values = []
|
||||
values += [
|
||||
|
@ -426,6 +427,9 @@ class Mobject(object):
|
|||
|
||||
### Getters ###
|
||||
|
||||
def get_points_defining_boundary(self):
|
||||
return self.points
|
||||
|
||||
def get_num_points(self):
|
||||
return len(self.points)
|
||||
|
||||
|
@ -517,7 +521,7 @@ class Mobject(object):
|
|||
|
||||
def submobject_family(self):
|
||||
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)
|
||||
|
||||
def family_members_with_points(self):
|
||||
|
|
|
@ -204,7 +204,8 @@ class VMobject(Mobject):
|
|||
it comes time to display.
|
||||
"""
|
||||
subpath_mobject = self.copy()#TODO, better way?
|
||||
# subpath_mobject = VMobject()
|
||||
subpath_mobject.submobjects = []
|
||||
# subpath_mobject = self.__class__()
|
||||
subpath_mobject.is_subpath = True
|
||||
subpath_mobject.set_points(points)
|
||||
self.add(subpath_mobject)
|
||||
|
@ -253,6 +254,13 @@ class VMobject(Mobject):
|
|||
for i in range(3)
|
||||
]
|
||||
|
||||
def get_anchors(self):
|
||||
return self.points[::3]
|
||||
|
||||
def get_points_defining_boundary(self):
|
||||
return self.get_anchors()
|
||||
|
||||
|
||||
## Alignment
|
||||
|
||||
def align_points(self, mobject):
|
||||
|
|
|
@ -17,67 +17,13 @@ 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 camera import Camera
|
||||
from mobject.svg_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):
|
||||
def construct(self):
|
||||
morty = Mortimer()
|
||||
|
|
|
@ -1357,7 +1357,8 @@ class FromRealToComplex(ComplexTransformationScene):
|
|||
VGroup(*lines[1::2]).highlight(RED)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
|
@ -3448,7 +3449,7 @@ class Thumbnail(ZetaTransformationScene):
|
|||
self.apply_zeta_function()
|
||||
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.to_edge(DOWN)
|
||||
div_sum.highlight(YELLOW)
|
||||
|
@ -3460,7 +3461,7 @@ class Thumbnail(ZetaTransformationScene):
|
|||
zeta.to_corner(UP+LEFT)
|
||||
|
||||
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.highlight(GREEN_B)
|
||||
million.add_background_rectangle()
|
||||
|
@ -3468,9 +3469,51 @@ class Thumbnail(ZetaTransformationScene):
|
|||
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:
|
||||
new_rate_func = squish_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_run_time(max_run_time)
|
||||
|
@ -337,6 +337,7 @@ class Scene(object):
|
|||
full_path = os.path.join(path, file_name)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
self.update_frame()
|
||||
Image.fromarray(self.get_frame()).save(full_path)
|
||||
|
||||
def get_movie_file_path(self, name, extension):
|
||||
|
|
|
@ -416,11 +416,15 @@ class TeacherStudentsScene(Scene):
|
|||
for x in range(num_times):
|
||||
pi_creature = random.choice(self.get_everyone())
|
||||
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):
|
||||
added_anims = kwargs.get("added_anims", [])
|
||||
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])
|
||||
target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
|
||||
self.play(
|
||||
|
|
|
@ -5,6 +5,10 @@ from mobject.vectorized_mobject import VGroup
|
|||
from mobject.svg_mobject import SVGMobject
|
||||
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):
|
||||
|
@ -50,6 +54,63 @@ class Headphones(SVGMobject):
|
|||
self.set_stroke(width = 0)
|
||||
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):
|
||||
CONFIG = {
|
||||
|
|
Loading…
Add table
Reference in a new issue