More Fourier + Heat animations

This commit is contained in:
Grant Sanderson
2019-04-06 11:52:56 -07:00
parent 5e7cbbdf24
commit 408da1871d
5 changed files with 339 additions and 38 deletions

View File

@ -10,6 +10,11 @@ ALL_SCENE_CLASSES = [
FourierOfTrebleClef,
FourierOfEighthNote,
FourierOfN,
FourierNailAndGear,
FourierNDQ,
FourierBatman,
FourierGoogleG,
FourierHeart,
# CirclesDrawingWave,
# Scenes for video
ExplainCircleAnimations,
@ -19,4 +24,7 @@ ALL_SCENE_CLASSES = [
FourierSeriesIntroBackground20,
FourierSeriesIntro,
PartTwoOfTour,
TwoDBodyWithManyTemperatures,
TwoDBodyWithManyTemperaturesGraph,
TwoDBodyWithManyTemperaturesContour,
]

View File

@ -15,6 +15,13 @@ class FourierCirclesScene(Scene):
"circle_style": {
"stroke_width": 2,
},
"arrow_config": {
"buff": 0,
"max_tip_length_to_length_ratio": 0.35,
"tip_length": 0.15,
"max_stroke_width_to_length_ratio": 10,
"stroke_width": 2,
},
"use_vectors": True,
"base_frequency": 1,
"slow_factor": 0.25,
@ -77,18 +84,21 @@ class FourierCirclesScene(Scene):
color=color,
**self.circle_style,
)
if self.use_vectors:
LineClass = Arrow
else:
LineClass = Line
circle.radial_line = LineClass(
line_points = (
circle.get_center(),
circle.get_start(),
color=WHITE,
buff=0,
max_tip_length_to_length_ratio=0.1,
**self.circle_style,
)
if self.use_vectors:
circle.radial_line = Arrow(
*line_points,
**self.arrow_config,
)
else:
circle.radial_line = Line(
*line_points,
color=WHITE,
**self.circle_style,
)
circle.add(circle.radial_line)
circle.freq = freq
circle.phase = phase
@ -154,10 +164,11 @@ class FourierCirclesScene(Scene):
width = 0
else:
width = stroke_width * (1 - (b % 1))
sp.set_stroke(YELLOW, width=width)
sp.set_stroke(width=width)
path.curr_time += dt
return path
broken_path.set_color(YELLOW)
broken_path.add_updater(update_path)
return broken_path
@ -365,13 +376,121 @@ class FourierOfEighthNote(FourierOfTrebleClef):
class FourierOfN(FourierOfTrebleClef):
CONFIG = {
"height": 6,
"n_circles": 200,
"n_circles": 1000,
}
def get_shape(self):
return TexMobject("N")
class FourierNailAndGear(FourierOfTrebleClef):
CONFIG = {
"height": 6,
"n_circles": 200,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
shape = SVGMobject("Nail_And_Gear")[1]
return shape
class FourierBatman(FourierOfTrebleClef):
CONFIG = {
"height": 4,
"n_circles": 100,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
shape = SVGMobject("BatmanLogo")[1]
return shape
class FourierHeart(FourierOfTrebleClef):
CONFIG = {
"height": 4,
"n_circles": 100,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
shape = SuitSymbol("hearts")
return shape
def get_drawn_path(self, *args, **kwargs):
kwargs["stroke_width"] = 5
path = super().get_drawn_path(*args, **kwargs)
path.set_color(PINK)
return path
class FourierNDQ(FourierOfTrebleClef):
CONFIG = {
"height": 4,
"n_circles": 1000,
"run_time": 10,
"arrow_config": {
"tip_length": 0.1,
"stroke_width": 2,
}
}
def get_shape(self):
path = VMobject()
shape = TexMobject("Hayley")
for sp in shape.family_members_with_points():
path.append_points(sp.points)
return path
class FourierGoogleG(FourierOfTrebleClef):
CONFIG = {
"n_circles": 10,
"height": 5,
"g_colors": [
"#4285F4",
"#DB4437",
"#F4B400",
"#0F9D58",
]
}
def get_shape(self):
g = SVGMobject("google_logo")[5]
g.center()
self.add(g)
return g
def get_drawn_path(self, *args, **kwargs):
kwargs["stroke_width"] = 7
path = super().get_drawn_path(*args, **kwargs)
blue, red, yellow, green = self.g_colors
path[:250].set_color(blue)
path[250:333].set_color(green)
path[333:370].set_color(yellow)
path[370:755].set_color(red)
path[755:780].set_color(yellow)
path[780:860].set_color(green)
path[860:].set_color(blue)
return path
class ExplainCircleAnimations(FourierCirclesScene):
CONFIG = {
"n_circles": 100,
@ -521,26 +640,25 @@ class ExplainCircleAnimations(FourierCirclesScene):
def show_as_vectors(self):
top_circles = self.top_circles
top_vectors = self.get_rotating_vectors(top_circles)
top_vectors.set_color(RED)
lines = VGroup(*filter(
lambda sm: isinstance(sm, Line),
top_circles.family_members_with_points()
))
top_vectors.set_color(WHITE)
self.add(lines, top_circles)
original_circles = top_circles.copy()
self.play(
FadeIn(top_vectors),
lines.fade, 1,
top_circles.set_opacity, 0,
)
self.wait(3)
self.play(
top_circles.match_style, original_circles
)
self.remove(top_vectors)
self.top_vectors = top_vectors
def show_vector_sum(self):
# trackers = self.center_trackers.deepcopy()
trackers = self.center_trackers.copy()
trackers.sort(
submob_func=lambda t: abs(t.circle.freq)
submob_func=lambda t: abs(t.circle.freq - 0.1)
)
plane = self.plane = NumberPlane(
x_min=-3,
@ -556,7 +674,6 @@ class ExplainCircleAnimations(FourierCirclesScene):
plane.move_to(self.center_point)
self.play(
# FadeOut(self.path),
FadeOut(self.drawn_path),
FadeOut(self.circles),
self.slow_factor_tracker.set_value, 0.05,
@ -565,7 +682,6 @@ class ExplainCircleAnimations(FourierCirclesScene):
self.play(FadeIn(plane))
new_circles = VGroup()
new_vectors = VGroup()
last_tracker = None
for tracker in trackers:
if last_tracker:
@ -574,20 +690,11 @@ class ExplainCircleAnimations(FourierCirclesScene):
tracker.new_location_func = lambda: self.center_point
original_circle = tracker.circle
original_vector = tracker.circle.vector
tracker.circle = original_circle.copy()
tracker.circle.center_func = tracker.get_location
tracker.vector = original_vector.copy()
tracker.vector.clear_updaters()
tracker.vector.circle = tracker.circle
tracker.vector.add_updater(lambda v: v.put_start_and_end_on(
v.circle.get_center(),
v.circle.get_start(),
))
new_circles.add(tracker.circle)
new_vectors.add(tracker.vector)
self.add(tracker, tracker.circle, tracker.vector)
self.add(tracker, tracker.circle)
start_point = tracker.get_location()
self.play(
UpdateFromAlphaFunc(
@ -613,7 +720,6 @@ class ExplainCircleAnimations(FourierCirclesScene):
self.slow_factor_tracker.set_value(0.1)
self.add(
self.top_circles,
self.top_vectors,
self.freq_numbers,
self.path,
)
@ -629,7 +735,6 @@ class ExplainCircleAnimations(FourierCirclesScene):
def tweak_starting_vectors(self):
top_circles = self.top_circles
top_vectors = self.top_vectors
circles = self.circles
path = self.path
drawn_path = self.drawn_path
@ -658,9 +763,7 @@ class ExplainCircleAnimations(FourierCirclesScene):
self.wait()
self.play(
ReplacementTransform(top_circles, new_top_circles),
ReplacementTransform(top_vectors, new_top_vectors),
ReplacementTransform(circles, new_circles),
# ReplacementTransform(path, new_path),
FadeOut(path),
run_time=3,
)

View File

@ -1,6 +1,156 @@
from big_ol_pile_of_manim_imports import *
from active_projects.ode.part2.shared_constructs import *
class NewSceneName(Scene):
class TwoDBodyWithManyTemperatures(ThreeDScene):
CONFIG = {
"cells_per_side": 20,
"body_height": 6,
}
def construct(self):
self.introduce_body()
self.show_temperature_at_all_points()
def introduce_body(self):
height = self.body_height
buff = 0.025
rows = VGroup(*[
VGroup(*[
Dot(
# stroke_width=0.5,
stroke_width=0,
fill_opacity=1,
)
for x in range(self.cells_per_side)
]).arrange(RIGHT, buff=buff)
for y in range(self.cells_per_side)
]).arrange(DOWN, buff=buff)
for row in rows[1::2]:
row.submobjects.reverse()
body = self.body = VGroup(*it.chain(*rows))
body.set_height(height)
body.center()
body.to_edge(LEFT)
axes = self.axes = Axes(
x_min=-5, x_max=5,
y_min=-5, y_max=5,
)
axes.match_height(body)
axes.move_to(body)
for cell in body:
self.color_cell(cell)
# body.set_stroke(WHITE, 0.5) # Do this?
plate = Square(
stroke_width=0,
fill_color=DARK_GREY,
sheen_direction=UL,
sheen_factor=1,
fill_opacity=1,
)
plate.replace(body)
plate_words = TextMobject("Piece of \\\\ metal")
plate_words.scale(2)
plate_words.set_stroke(BLACK, 2, background=True)
plate_words.set_color(BLACK)
plate_words.move_to(plate)
self.play(
DrawBorderThenFill(plate),
Write(
plate_words,
run_time=2,
rate_func=squish_rate_func(smooth, 0.5, 1)
)
)
self.wait()
self.remove(plate_words)
def show_temperature_at_all_points(self):
body = self.body
start_corner = body[0].get_center()
dot = Dot(radius=0.01, color=WHITE)
dot.move_to(start_corner)
get_point = dot.get_center
lhs = TexMobject("T = ")
lhs.next_to(body, RIGHT, LARGE_BUFF)
decimal = DecimalNumber(
num_decimal_places=1,
unit="^\\circ"
)
decimal.next_to(lhs, RIGHT, MED_SMALL_BUFF, DOWN)
decimal.add_updater(
lambda d: d.set_value(
40 + 50 * self.point_to_temp(get_point())
)
)
arrow = Arrow(color=YELLOW)
arrow.set_stroke(BLACK, 8, background=True)
arrow.tip.set_stroke(BLACK, 2, background=True)
# arrow.add_to_back(arrow.copy().set_stroke(BLACK, 5))
arrow.add_updater(lambda a: a.put_start_and_end_on(
lhs.get_left() + MED_SMALL_BUFF * LEFT,
get_point(),
))
dot.add_updater(lambda p: p.move_to(
body[-1] if (1 < len(body)) else start_corner
))
self.add(body, dot, lhs, decimal, arrow)
self.play(
ShowIncreasingSubsets(
body,
run_time=10,
rate_func=linear,
)
)
self.wait()
self.remove(dot)
self.play(
FadeOut(arrow),
FadeOut(lhs),
FadeOut(decimal),
)
#
def point_to_temp(self, point, time=0):
x, y = self.axes.point_to_coords(point)
return two_d_temp_func(
0.3 * x, 0.3 * y, t=time
)
def color_cell(self, cell, vect=RIGHT):
p0 = cell.get_corner(-vect)
p1 = cell.get_corner(vect)
colors = []
for point in p0, p1:
temp = self.point_to_temp(point)
color = temperature_to_color(temp)
colors.append(color)
cell.set_color(color=colors)
cell.set_sheen_direction(vect)
return cell
class TwoDBodyWithManyTemperaturesGraph(ExternallyAnimatedScene):
pass
class TwoDBodyWithManyTemperaturesContour(ExternallyAnimatedScene):
pass
class BringTwoRodsTogether(Scene):
def construct(self):
pass

View File

@ -0,0 +1,35 @@
from big_ol_pile_of_manim_imports import *
TIME_COLOR = YELLOW
X_COLOR = GREEN
def get_heat_equation():
pass
def temperature_to_color(temp, min_temp=-1, max_temp=1):
colors = [BLUE, TEAL, GREEN, YELLOW, "#ff0000"]
alpha = inverse_interpolate(min_temp, max_temp, temp)
index, sub_alpha = integer_interpolate(
0, len(colors) - 1, alpha
)
return interpolate_color(
colors[index], colors[index + 1], sub_alpha
)
def two_d_temp_func(x, y, t):
return np.sum([
c * np.sin(f * var) * np.exp(-(f**2) * t)
for c, f, var in [
(0.2, 1, x),
(0.3, 3, x),
(0.02, 5, x),
(0.01, 7, x),
(0.5, 2, y),
(0.1, 10, y),
(0.01, 20, y),
]
])

View File

@ -56,7 +56,7 @@ class FourierSeriesIntro(Scene):
TransformFromCopy(
title.get_part_by_tex("Fourier"),
name.get_part_by_tex("Fourier"),
path_arc=-45 * DEGREES,
path_arc=90 * DEGREES,
),
FadeIn(name.get_part_by_tex("Joseph")),
)
@ -81,3 +81,8 @@ class PartTwoOfTour(TourOfDifferentialEquations):
CONFIG = {
"zoomed_thumbnail_index": 1,
}
class CompareODEToPDE(Scene):
def construct(self):
pass