mirror of
https://github.com/3b1b/manim.git
synced 2025-08-02 02:35:22 +08:00
243 lines
7.1 KiB
Python
243 lines
7.1 KiB
Python
from manimlib.imports import *
|
|
|
|
from active_projects.ode.part2.fourier_series import FourierOfTrebleClef
|
|
|
|
|
|
class ComplexFourierSeriesExample(FourierOfTrebleClef):
|
|
CONFIG = {
|
|
"file_name": "EighthNote",
|
|
"run_time": 10,
|
|
"n_vectors": 200,
|
|
"n_cycles": 2,
|
|
"max_circle_stroke_width": 0.75,
|
|
"drawing_height": 5,
|
|
"center_point": DOWN,
|
|
"top_row_y": 3,
|
|
"top_row_label_y": 2,
|
|
"top_row_x_spacing": 1.75,
|
|
"top_row_copy_scale_factor": 0.9,
|
|
"start_drawn": False,
|
|
}
|
|
|
|
def construct(self):
|
|
self.add_vectors_circles_path()
|
|
self.add_top_row(self.vectors, self.circles)
|
|
self.write_title()
|
|
self.highlight_vectors_one_by_one()
|
|
self.change_shape()
|
|
|
|
def write_title(self):
|
|
title = TextMobject("Complex\\\\Fourier series")
|
|
title.scale(1.5)
|
|
title.to_edge(LEFT)
|
|
title.match_y(self.path)
|
|
|
|
self.wait(5)
|
|
self.play(FadeInFromDown(title))
|
|
self.wait(2)
|
|
self.title = title
|
|
|
|
def highlight_vectors_one_by_one(self):
|
|
# Don't know why these vectors can't get copied.
|
|
# That seems like a problem that will come up again.
|
|
labels = self.top_row[-1]
|
|
next_anims = []
|
|
for vector, circle, label in zip(self.vectors, self.circles, labels):
|
|
# v_color = vector.get_color()
|
|
c_color = circle.get_color()
|
|
c_stroke_width = circle.get_stroke_width()
|
|
|
|
rect = SurroundingRectangle(label, color=PINK)
|
|
self.play(
|
|
# vector.set_color, PINK,
|
|
circle.set_stroke, RED, 3,
|
|
FadeIn(rect),
|
|
*next_anims
|
|
)
|
|
self.wait()
|
|
next_anims = [
|
|
# vector.set_color, v_color,
|
|
circle.set_stroke, c_color, c_stroke_width,
|
|
FadeOut(rect),
|
|
]
|
|
self.play(*next_anims)
|
|
|
|
def change_shape(self):
|
|
# path_mob = TexMobject("\\pi")
|
|
path_mob = SVGMobject("Nail_And_Gear")
|
|
new_path = path_mob.family_members_with_points()[0]
|
|
new_path.set_height(4)
|
|
new_path.move_to(self.path, DOWN)
|
|
new_path.shift(0.5 * UP)
|
|
new_coefs = self.get_coefficients_of_path(new_path)
|
|
new_vectors = self.get_rotating_vectors(
|
|
coefficients=new_coefs
|
|
)
|
|
new_drawn_path = self.get_drawn_path(new_vectors)
|
|
|
|
self.vector_clock.set_value(0)
|
|
self.vector_clock.suspend_updating(0)
|
|
|
|
vectors = self.vectors
|
|
anims = []
|
|
|
|
for vect, new_vect in zip(vectors, new_vectors):
|
|
new_vect.update()
|
|
new_vect.clear_updaters()
|
|
|
|
line = Line(stroke_width=0)
|
|
line.put_start_and_end_on(*vect.get_start_and_end())
|
|
anims.append(ApplyMethod(
|
|
line.put_start_and_end_on,
|
|
*new_vect.get_start_and_end()
|
|
))
|
|
vect.freq = new_vect.freq
|
|
vect.phase = new_vect.phase
|
|
vect.coefficient = new_vect.coefficient
|
|
|
|
vect.line = line
|
|
vect.add_updater(
|
|
lambda v: v.put_start_and_end_on(
|
|
*v.line.get_start_and_end()
|
|
)
|
|
)
|
|
anims += [
|
|
FadeOut(self.drawn_path)
|
|
]
|
|
|
|
self.play(*anims, run_time=3)
|
|
self.vector_clock.resume_updating()
|
|
for vect in self.vectors:
|
|
vect.remove_updater(vect.updaters[-1])
|
|
|
|
self.add(new_drawn_path)
|
|
for n in range(self.n_cycles):
|
|
self.run_one_cycle()
|
|
|
|
#
|
|
def get_path(self):
|
|
path = super().get_path()
|
|
path.set_height(self.drawing_height)
|
|
path.to_edge(DOWN)
|
|
return path
|
|
|
|
def add_top_row(self, vectors, circles, max_freq=3):
|
|
self.top_row = self.get_top_row(
|
|
vectors, circles, max_freq
|
|
)
|
|
self.add(self.top_row)
|
|
|
|
def get_top_row(self, vectors, circles, max_freq=3):
|
|
vector_copies = VGroup()
|
|
circle_copies = VGroup()
|
|
for vector, circle in zip(vectors, circles):
|
|
if vector.freq > max_freq:
|
|
break
|
|
vcopy = vector.copy()
|
|
vcopy.clear_updaters()
|
|
ccopy = circle.copy()
|
|
ccopy.clear_updaters()
|
|
ccopy.original = circle
|
|
vcopy.original = vector
|
|
|
|
vcopy.center_point = np.array([
|
|
vector.freq * self.top_row_x_spacing,
|
|
self.top_row_y,
|
|
0
|
|
])
|
|
ccopy.center_point = vcopy.center_point
|
|
vcopy.add_updater(self.update_top_row_vector_copy)
|
|
ccopy.add_updater(self.update_top_row_circle_copy)
|
|
vector_copies.add(vcopy)
|
|
circle_copies.add(ccopy)
|
|
|
|
dots = VGroup(*[
|
|
TexMobject("\\dots").next_to(
|
|
circle_copies, direction,
|
|
MED_LARGE_BUFF,
|
|
)
|
|
for direction in [LEFT, RIGHT]
|
|
])
|
|
labels = self.get_top_row_labels(vector_copies)
|
|
return VGroup(
|
|
vector_copies,
|
|
circle_copies,
|
|
dots,
|
|
labels,
|
|
)
|
|
|
|
def update_top_row_vector_copy(self, vcopy):
|
|
vcopy.become(vcopy.original)
|
|
vcopy.scale(self.top_row_copy_scale_factor)
|
|
vcopy.shift(vcopy.center_point - vcopy.get_start())
|
|
return vcopy
|
|
|
|
def update_top_row_circle_copy(self, ccopy):
|
|
ccopy.become(ccopy.original)
|
|
ccopy.scale(self.top_row_copy_scale_factor)
|
|
ccopy.move_to(ccopy.center_point)
|
|
return ccopy
|
|
|
|
def get_top_row_labels(self, vector_copies):
|
|
labels = VGroup()
|
|
for vector_copy in vector_copies:
|
|
freq = vector_copy.freq
|
|
label = Integer(freq)
|
|
label.move_to(np.array([
|
|
freq * self.top_row_x_spacing,
|
|
self.top_row_label_y,
|
|
0
|
|
]))
|
|
labels.add(label)
|
|
return labels
|
|
|
|
|
|
class ComplexFourierSeriesExampleEnd(ExternallyAnimatedScene):
|
|
pass
|
|
|
|
|
|
class FourierSeriesExampleWithRectForZoom(ComplexFourierSeriesExample):
|
|
CONFIG = {
|
|
"n_vectors": 100,
|
|
"slow_factor": 0.01,
|
|
"rect_scale_factor": 0.15,
|
|
"parametric_function_step_size": 0.0001,
|
|
"start_drawn": True,
|
|
}
|
|
|
|
def construct(self):
|
|
self.add_vectors_circles_path()
|
|
self.circles.set_stroke(opacity=0.5)
|
|
rect = self.get_rect()
|
|
rect.set_height(self.rect_scale_factor * FRAME_HEIGHT)
|
|
rect.add_updater(lambda m: m.move_to(
|
|
center_of_mass([
|
|
v.get_end()
|
|
for v in self.vectors
|
|
])
|
|
))
|
|
self.add(rect)
|
|
self.run_one_cycle()
|
|
|
|
def get_rect(self):
|
|
return ScreenRectangle(
|
|
color=WHITE,
|
|
stroke_width=2,
|
|
)
|
|
|
|
|
|
class ZoomedInFourierSeriesExample(FourierSeriesExampleWithRectForZoom, MovingCameraScene):
|
|
CONFIG = {
|
|
"vector_config": {
|
|
"max_tip_length_to_length_ratio": 0.15,
|
|
"tip_length": 0.05,
|
|
}
|
|
}
|
|
|
|
def setup(self):
|
|
ComplexFourierSeriesExample.setup(self)
|
|
MovingCameraScene.setup(self)
|
|
|
|
def get_rect(self):
|
|
return self.camera_frame
|