mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Merge branch 'master' of https://github.com/3b1b/manim into WindingNumber
This commit is contained in:
commit
d24eab9979
3 changed files with 254 additions and 27 deletions
|
@ -59,6 +59,13 @@ def get_fourier_graph(
|
||||||
for x, y in zip(frequencies, fft_output[:n_samples//2])
|
for x, y in zip(frequencies, fft_output[:n_samples//2])
|
||||||
])
|
])
|
||||||
graph.highlight(color)
|
graph.highlight(color)
|
||||||
|
f_min, f_max = [
|
||||||
|
axes.x_axis.point_to_number(graph.points[i])
|
||||||
|
for i in 0, -1
|
||||||
|
]
|
||||||
|
graph.underlying_function = lambda f : axes.y_axis.point_to_number(
|
||||||
|
graph.point_from_proportion((f - f_min)/(f_max - f_min))
|
||||||
|
)
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
def get_fourier_transform(
|
def get_fourier_transform(
|
||||||
|
|
|
@ -153,7 +153,7 @@ class Plane(SVGMobject):
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
SVGMobject.__init__(self, **kwargs)
|
SVGMobject.__init__(self, **kwargs)
|
||||||
self.rotate(-TAU/8)
|
self.rotate(-TAU/4)
|
||||||
|
|
||||||
class FalconHeavy(SVGMobject):
|
class FalconHeavy(SVGMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -2302,11 +2302,12 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
}
|
}
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.setup_axes()
|
self.setup_axes()
|
||||||
|
self.setup_objects()
|
||||||
self.send_long_pulse_single_echo()
|
self.send_long_pulse_single_echo()
|
||||||
self.introduce_multiple_objects()
|
self.introduce_multiple_objects()
|
||||||
self.use_short_pulse()
|
self.use_short_pulse()
|
||||||
self.transition_to_frequency_view()
|
|
||||||
self.fourier_transform_of_one_pulse()
|
self.fourier_transform_of_one_pulse()
|
||||||
|
self.show_echos_of_moving_objects()
|
||||||
self.overlapping_frequenies_of_various_objects()
|
self.overlapping_frequenies_of_various_objects()
|
||||||
self.echos_of_long_pure_signal_in_frequency_space()
|
self.echos_of_long_pure_signal_in_frequency_space()
|
||||||
self.concentrated_fourier_requires_long_time()
|
self.concentrated_fourier_requires_long_time()
|
||||||
|
@ -2314,7 +2315,7 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
def setup_axes(self):
|
def setup_axes(self):
|
||||||
axes = self.axes = Axes(
|
axes = self.axes = Axes(
|
||||||
x_min = 0, x_max = 10,
|
x_min = 0, x_max = 10,
|
||||||
y_min = -2, y_max = 2,
|
y_min = -1.5, y_max = 1.5,
|
||||||
)
|
)
|
||||||
time_label = TextMobject("Time")
|
time_label = TextMobject("Time")
|
||||||
time_label.next_to(axes.x_axis.get_right(), UP)
|
time_label.next_to(axes.x_axis.get_right(), UP)
|
||||||
|
@ -2328,6 +2329,7 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
dish.to_edge(UP, buff = LARGE_BUFF)
|
dish.to_edge(UP, buff = LARGE_BUFF)
|
||||||
self.add(dish)
|
self.add(dish)
|
||||||
|
|
||||||
|
def setup_objects(self):
|
||||||
objects = self.objects = VGroup(
|
objects = self.objects = VGroup(
|
||||||
Plane().flip(),
|
Plane().flip(),
|
||||||
SVGMobject(
|
SVGMobject(
|
||||||
|
@ -2337,7 +2339,7 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
),
|
),
|
||||||
SVGMobject(
|
SVGMobject(
|
||||||
file_name = "biplane",
|
file_name = "biplane",
|
||||||
color = average_color(DARK_GREY, RED),
|
color = RED_D,
|
||||||
height = 0.5,
|
height = 0.5,
|
||||||
),
|
),
|
||||||
SVGMobject(
|
SVGMobject(
|
||||||
|
@ -2347,12 +2349,20 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
).rotate(-TAU/24),
|
).rotate(-TAU/24),
|
||||||
FalconHeavy(),
|
FalconHeavy(),
|
||||||
)
|
)
|
||||||
y_shifts = [0.25, 0, 0.5, 0.25, -0.25]
|
y_shifts = [0.25, 0, 0.5, 0.25, -0.5]
|
||||||
for x, y, obj in zip(self.object_x_coords, y_shifts, objects):
|
for x, y, obj in zip(self.object_x_coords, y_shifts, objects):
|
||||||
obj.move_to(self.axes.coords_to_point(x, 0))
|
obj.move_to(self.axes.coords_to_point(x, 0))
|
||||||
obj.align_to(self.dish)
|
obj.align_to(self.dish)
|
||||||
obj.shift(y*UP)
|
obj.shift(y*UP)
|
||||||
|
|
||||||
|
self.object_velocities = [
|
||||||
|
0.7*LEFT,
|
||||||
|
0.1*RIGHT,
|
||||||
|
0.4*LEFT,
|
||||||
|
0.4*RIGHT,
|
||||||
|
0.5*UP,
|
||||||
|
]
|
||||||
|
|
||||||
def send_long_pulse_single_echo(self):
|
def send_long_pulse_single_echo(self):
|
||||||
x = self.object_x_coords[0]
|
x = self.object_x_coords[0]
|
||||||
plane = self.objects[0]
|
plane = self.objects[0]
|
||||||
|
@ -2501,22 +2511,197 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
)
|
)
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
self.add(sum_graph)
|
self.curr_graph = sum_graph
|
||||||
|
self.first_echo_graph = graphs[0]
|
||||||
def transition_to_frequency_view(self):
|
self.first_echo_graph.highlight(YELLOW)
|
||||||
pass
|
|
||||||
|
|
||||||
def fourier_transform_of_one_pulse(self):
|
def fourier_transform_of_one_pulse(self):
|
||||||
pass
|
frequency_axes = Axes(
|
||||||
|
x_min = 0, x_max = 20,
|
||||||
|
x_axis_config = {
|
||||||
|
"unit_size" : 0.5,
|
||||||
|
"tick_frequency" : 2,
|
||||||
|
},
|
||||||
|
y_min = -.01, y_max = .01,
|
||||||
|
y_axis_config = {
|
||||||
|
"unit_size" : 110,
|
||||||
|
"tick_frequency" : 0.006
|
||||||
|
}
|
||||||
|
)
|
||||||
|
frequency_label = TextMobject("Frequency")
|
||||||
|
frequency_label.next_to(frequency_axes.x_axis.get_right(), UP)
|
||||||
|
frequency_axes.add(frequency_label)
|
||||||
|
first_echo_graph = self.first_echo_graph
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
ApplyMethod(
|
||||||
|
VGroup(self.axes, first_echo_graph).to_edge, UP,
|
||||||
|
{"buff" : SMALL_BUFF},
|
||||||
|
rate_func = squish_rate_func(smooth, 0.5, 1)
|
||||||
|
),
|
||||||
|
LaggedStart(FadeOut, self.objects),
|
||||||
|
LaggedStart(FadeOut, VGroup(
|
||||||
|
self.curr_graph, self.dish, self.pi_creature
|
||||||
|
)),
|
||||||
|
run_time = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
frequency_axes.next_to(self.axes, DOWN, LARGE_BUFF, LEFT)
|
||||||
|
fourier_graph = get_fourier_graph(
|
||||||
|
frequency_axes, first_echo_graph.underlying_function,
|
||||||
|
t_min = 0, t_max = 25,
|
||||||
|
complex_to_real_func = np.abs,
|
||||||
|
)
|
||||||
|
fourier_graph.save_state()
|
||||||
|
fourier_graph.move_to(first_echo_graph)
|
||||||
|
h_vect = 4*RIGHT
|
||||||
|
fourier_graph.shift(h_vect)
|
||||||
|
fourier_graph.fade(1)
|
||||||
|
|
||||||
|
f = 8
|
||||||
|
v_line = DashedLine(
|
||||||
|
frequency_axes.coords_to_point(f, 0),
|
||||||
|
frequency_axes.coords_to_point(f, frequency_axes.y_max),
|
||||||
|
)
|
||||||
|
v_lines = VGroup(
|
||||||
|
v_line.copy().shift(2*LEFT),
|
||||||
|
v_line.copy().shift(2*RIGHT),
|
||||||
|
)
|
||||||
|
rect = Rectangle(stroke_width = 0, fill_color = YELLOW, fill_opacity = 0.25)
|
||||||
|
rect.replace(v_lines, stretch = True)
|
||||||
|
rect.save_state()
|
||||||
|
rect.stretch(0, 0)
|
||||||
|
|
||||||
|
self.play(Write(frequency_axes, run_time = 1))
|
||||||
|
self.play(
|
||||||
|
ApplyFunction(
|
||||||
|
lambda m : m.move_to(fourier_graph.saved_state).shift(-h_vect).fade(1),
|
||||||
|
first_echo_graph.copy(),
|
||||||
|
remover = True,
|
||||||
|
),
|
||||||
|
fourier_graph.restore
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(ShowCreation(v_line))
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(VGroup(v_line), v_lines),
|
||||||
|
rect.restore
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(FadeOut(v_lines), FadeOut(rect))
|
||||||
|
|
||||||
|
self.frequency_axes = frequency_axes
|
||||||
|
self.fourier_graph = fourier_graph
|
||||||
|
|
||||||
|
def show_echos_of_moving_objects(self):
|
||||||
|
objects = self.objects
|
||||||
|
objects.save_state()
|
||||||
|
object_velocities = self.object_velocities
|
||||||
|
|
||||||
|
movements = self.object_movements = [
|
||||||
|
AmbientMovement(
|
||||||
|
obj,
|
||||||
|
direction = v/np.linalg.norm(v),
|
||||||
|
rate = np.linalg.norm(v)
|
||||||
|
)
|
||||||
|
for v, obj in zip(object_velocities, objects)
|
||||||
|
]
|
||||||
|
pulses = self.get_pulses()
|
||||||
|
continual_anims = pulses+movements
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeOut(self.axes),
|
||||||
|
FadeOut(self.first_echo_graph),
|
||||||
|
LaggedStart(FadeIn, objects),
|
||||||
|
FadeIn(self.dish)
|
||||||
|
)
|
||||||
|
self.add(*continual_anims)
|
||||||
|
self.wait(4)
|
||||||
|
self.play(*[
|
||||||
|
UpdateFromAlphaFunc(
|
||||||
|
obj,
|
||||||
|
lambda m, a : m.set_fill(opacity = 1-a),
|
||||||
|
)
|
||||||
|
for obj in objects
|
||||||
|
])
|
||||||
|
self.remove(*continual_anims)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
def overlapping_frequenies_of_various_objects(self):
|
def overlapping_frequenies_of_various_objects(self):
|
||||||
pass
|
frequency_axes = self.frequency_axes
|
||||||
|
fourier_graph = self.fourier_graph
|
||||||
|
shifted_graphs = self.get_shifted_frequency_graphs(fourier_graph)
|
||||||
|
color = fourier_graph.get_color()
|
||||||
|
shifted_graphs.gradient_highlight(
|
||||||
|
average_color(color, WHITE),
|
||||||
|
color,
|
||||||
|
average_color(color, BLACK),
|
||||||
|
)
|
||||||
|
sum_graph = self.get_sum_graph(frequency_axes, shifted_graphs)
|
||||||
|
sum_graph.match_style(fourier_graph)
|
||||||
|
|
||||||
|
shifted_graphs.save_state()
|
||||||
|
|
||||||
|
self.play(ReplacementTransform(
|
||||||
|
VGroup(fourier_graph), shifted_graphs,
|
||||||
|
submobject_mode = "lagged_start",
|
||||||
|
run_time = 2
|
||||||
|
))
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
shifted_graphs.arrange_submobjects, DOWN,
|
||||||
|
shifted_graphs.move_to, fourier_graph, DOWN,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(shifted_graphs.restore),
|
||||||
|
self.play(ReplacementTransform(
|
||||||
|
shifted_graphs, VGroup(sum_graph),
|
||||||
|
))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.curr_fourier_graph = sum_graph
|
||||||
|
|
||||||
def echos_of_long_pure_signal_in_frequency_space(self):
|
def echos_of_long_pure_signal_in_frequency_space(self):
|
||||||
pass
|
curr_fourier_graph = self.curr_fourier_graph
|
||||||
|
f_max = self.frequency_axes.y_max
|
||||||
|
new_fourier_graph = self.frequency_axes.get_graph(
|
||||||
|
lambda x : f_max * np.exp(-100*(x-8)**2),
|
||||||
|
num_graph_points = 1000,
|
||||||
|
)
|
||||||
|
new_fourier_graph.highlight(PINK)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeOut(curr_fourier_graph),
|
||||||
|
FadeIn(new_fourier_graph),
|
||||||
|
)
|
||||||
|
self.fourier_graph = new_fourier_graph
|
||||||
|
self.overlapping_frequenies_of_various_objects()
|
||||||
|
|
||||||
def concentrated_fourier_requires_long_time(self):
|
def concentrated_fourier_requires_long_time(self):
|
||||||
pass
|
objects = self.objects
|
||||||
|
objects.restore()
|
||||||
|
object_movements = self.object_movements
|
||||||
|
self.n_pulse_singletons = 32
|
||||||
|
pulses = self.get_pulses()
|
||||||
|
randy = self.pi_creature
|
||||||
|
|
||||||
|
continual_anims = object_movements+pulses
|
||||||
|
self.play(FadeIn(randy))
|
||||||
|
self.add(*continual_anims)
|
||||||
|
self.play(randy.change, "angry", *[
|
||||||
|
UpdateFromAlphaFunc(obj, lambda m, a : m.set_fill(opacity = a))
|
||||||
|
for obj in objects
|
||||||
|
])
|
||||||
|
self.play(Blink(randy))
|
||||||
|
self.wait(2)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
self.wait()
|
||||||
|
self.play(randy.change, "plain", *[
|
||||||
|
UpdateFromAlphaFunc(obj, lambda m, a : m.set_fill(opacity = 1-a))
|
||||||
|
for obj in objects
|
||||||
|
])
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -2538,7 +2723,7 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
def get_pulses(self):
|
def get_pulses(self):
|
||||||
return [
|
return [
|
||||||
self.get_pulse(
|
self.get_pulse(
|
||||||
self.dish.copy(),#.align_to(obj),
|
self.dish.copy().shift(0.01*obj.get_center()[0]),
|
||||||
obj
|
obj
|
||||||
)
|
)
|
||||||
for obj in self.objects
|
for obj in self.objects
|
||||||
|
@ -2550,9 +2735,33 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene):
|
||||||
randy.to_edge(RIGHT, buff = 1.7).shift(0.5*UP)
|
randy.to_edge(RIGHT, buff = 1.7).shift(0.5*UP)
|
||||||
return randy
|
return randy
|
||||||
|
|
||||||
|
def get_shifted_frequency_graphs(self, fourier_graph):
|
||||||
|
frequency_axes = self.frequency_axes
|
||||||
|
def get_func(v):
|
||||||
|
return lambda f : fourier_graph.underlying_function(np.clip(
|
||||||
|
f-5*v[0],
|
||||||
|
frequency_axes.x_min,
|
||||||
|
frequency_axes.x_max,
|
||||||
|
))
|
||||||
|
def get_graph(func):
|
||||||
|
return frequency_axes.get_graph(func)
|
||||||
|
shifted_graphs = VGroup(*map(
|
||||||
|
get_graph, map(get_func, self.object_velocities)
|
||||||
|
))
|
||||||
|
shifted_graphs.match_style(fourier_graph)
|
||||||
|
return shifted_graphs
|
||||||
|
|
||||||
|
def get_sum_graph(self, axes, graphs):
|
||||||
|
def get_func(graph):
|
||||||
|
return graph.underlying_function
|
||||||
|
funcs = map(get_func, graphs)
|
||||||
|
return axes.get_graph(
|
||||||
|
lambda t : sum([func(t) for func in funcs]),
|
||||||
|
)
|
||||||
|
|
||||||
|
class SummarizeFourierTradeoffForDoppler(Scene):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import itertools as it
|
||||||
|
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
import warnings
|
||||||
from mobject import Mobject, Group
|
from mobject import Mobject, Group
|
||||||
from mobject.vectorized_mobject import VMobject
|
from mobject.vectorized_mobject import VMobject
|
||||||
from mobject.tex_mobject import TextMobject
|
from mobject.tex_mobject import TextMobject
|
||||||
|
@ -407,8 +408,8 @@ class Succession(Animation):
|
||||||
self.run_times = [anim.run_time for anim in animations]
|
self.run_times = [anim.run_time for anim in animations]
|
||||||
if "run_time" in kwargs:
|
if "run_time" in kwargs:
|
||||||
run_time = kwargs.pop("run_time")
|
run_time = kwargs.pop("run_time")
|
||||||
else:
|
warnings.warn("Succession doesn't currently support explicit run_time.")
|
||||||
run_time = sum(self.run_times)
|
run_time = sum(self.run_times)
|
||||||
self.num_anims = len(animations)
|
self.num_anims = len(animations)
|
||||||
if self.num_anims == 0:
|
if self.num_anims == 0:
|
||||||
self.empty = True
|
self.empty = True
|
||||||
|
@ -463,11 +464,21 @@ class Succession(Animation):
|
||||||
self.current_alpha = alpha
|
self.current_alpha = alpha
|
||||||
return
|
return
|
||||||
|
|
||||||
i = 0
|
gt_alpha_list = filter(
|
||||||
while self.critical_alphas[i + 1] < alpha:
|
lambda i : self.critical_alphas[i+1] >= alpha,
|
||||||
i = i + 1
|
range(len(self.critical_alphas)-1)
|
||||||
# TODO: Special handling if alpha < 0 or alpha > 1, to use
|
)
|
||||||
# first or last sub-animation
|
if gt_alpha_list:
|
||||||
|
i = gt_alpha_list[0]
|
||||||
|
else:
|
||||||
|
if not abs(alpha - 1) < 0.001:
|
||||||
|
warnings.warn(
|
||||||
|
"Rounding error not near alpha=1 in Succession.update_mobject," + \
|
||||||
|
"instead alpha = %f"%alpha
|
||||||
|
)
|
||||||
|
print self.critical_alphas, alpha
|
||||||
|
i = len(self.critical_alphas) - 2
|
||||||
|
#
|
||||||
|
|
||||||
# At this point, we should have self.critical_alphas[i] <= alpha <= self.critical_alphas[i +1]
|
# At this point, we should have self.critical_alphas[i] <= alpha <= self.critical_alphas[i +1]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue