mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 08:54:38 +08:00
More Fourier + Heat animations
This commit is contained in:
@ -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,
|
||||
]
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
35
active_projects/ode/part2/shared_constructs.py
Normal file
35
active_projects/ode/part2/shared_constructs.py
Normal 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),
|
||||
]
|
||||
])
|
@ -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
|
||||
|
Reference in New Issue
Block a user