mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Preditor prey scenes
This commit is contained in:
parent
3550ec3234
commit
719f24c0dc
1 changed files with 298 additions and 28 deletions
|
@ -13,6 +13,7 @@ DEFAULT_SCALAR_FIELD_COLORS = [BLUE_E, GREEN, YELLOW, RED]
|
|||
# the stream lines. Certainly while developing, things were not
|
||||
# run at production quality.
|
||||
|
||||
FOX_COLOR = "#DF7F20"
|
||||
|
||||
# Helper functions
|
||||
def get_flow_start_points(x_min=-8, x_max=8,
|
||||
|
@ -221,8 +222,17 @@ def get_electron(radius=0.05):
|
|||
|
||||
|
||||
def preditor_prey_vector_field(point):
|
||||
alpha = 30.0
|
||||
beta = 1.0
|
||||
gamma = 30.0
|
||||
delta = 1.0
|
||||
x, y = point[:2]
|
||||
return -(y - 30) * RIGHT + (x - 30) * UP
|
||||
result = 0.05 * np.array([
|
||||
alpha * x - beta * x * y,
|
||||
delta * x * y - gamma * y,
|
||||
0,
|
||||
])
|
||||
return rotate(result, 1 * DEGREES)
|
||||
|
||||
# Mobjects
|
||||
|
||||
|
@ -2386,6 +2396,9 @@ class IllustrateGaussMagnetic(IllustrateGaussLaw):
|
|||
"colors": [BLUE_E, BLUE_D, BLUE_C],
|
||||
"stroke_width": 3,
|
||||
},
|
||||
"stream_line_animation_config": {
|
||||
"start_up_time": 0,
|
||||
},
|
||||
"flow_time": 10,
|
||||
}
|
||||
|
||||
|
@ -2426,12 +2439,14 @@ class IllustrateGaussMagnetic(IllustrateGaussLaw):
|
|||
|
||||
def func(self, point):
|
||||
x, y = point[:2]
|
||||
top_part = np.array([(y - 0.25), -x, 0])
|
||||
bottom_part = np.array([-(y + 0.25), x, 0])
|
||||
top_part = np.array([(y - 1.0), -x, 0])
|
||||
bottom_part = np.array([-(y + 1.0), x, 0])
|
||||
norm = np.linalg.norm
|
||||
return 3 * op.add(
|
||||
top_part / (norm(top_part)**2 + 1),
|
||||
bottom_part / (norm(bottom_part)**2 + 1),
|
||||
return 1 * op.add(
|
||||
top_part / (norm(top_part) * norm(point - UP) + 0.1),
|
||||
bottom_part / (norm(bottom_part) * norm(point - DOWN) + 0.1),
|
||||
# top_part / (norm(top_part)**2 + 1),
|
||||
# bottom_part / (norm(bottom_part)**2 + 1),
|
||||
)
|
||||
|
||||
|
||||
|
@ -2457,9 +2472,9 @@ class RelevantInNonSpatialCircumstances(TeacherStudentsScene):
|
|||
|
||||
class ShowTwoPopulations(Scene):
|
||||
CONFIG = {
|
||||
"total_num_animals": 50,
|
||||
"start_num_foxes": 20,
|
||||
"start_num_rabbits": 30,
|
||||
"total_num_animals": 80,
|
||||
"start_num_foxes": 40,
|
||||
"start_num_rabbits": 20,
|
||||
"animal_height": 0.5,
|
||||
"final_wait_time": 30,
|
||||
}
|
||||
|
@ -2484,7 +2499,7 @@ class ShowTwoPopulations(Scene):
|
|||
for mob in examples:
|
||||
mob.save_state()
|
||||
mob.scale_to_fit_height(3)
|
||||
examples.arrange_submobjects(RIGHT, buff=2)
|
||||
examples.arrange_submobjects(LEFT, buff=2)
|
||||
|
||||
preditor, prey = words = VGroup(
|
||||
TextMobject("Preditor"),
|
||||
|
@ -2529,10 +2544,10 @@ class ShowTwoPopulations(Scene):
|
|||
preditor_prey_vector_field,
|
||||
))
|
||||
|
||||
def get_num_foxes():
|
||||
def get_num_rabbits():
|
||||
return phase_point.get_center()[0]
|
||||
|
||||
def get_num_rabbits():
|
||||
def get_num_foxes():
|
||||
return phase_point.get_center()[1]
|
||||
|
||||
def get_updater(pop_size_getter):
|
||||
|
@ -2592,9 +2607,14 @@ class ShowTwoPopulations(Scene):
|
|||
file_name=name,
|
||||
height=self.animal_height,
|
||||
fill_color=color,
|
||||
# stroke_color=BLACK,
|
||||
# stroke_width=0.5
|
||||
)
|
||||
for submob in result.family_members_with_points():
|
||||
if submob.is_subpath:
|
||||
submob.is_subpath = False
|
||||
submob.set_fill(
|
||||
interpolate_color(color, BLACK, 0.8),
|
||||
opacity=1
|
||||
)
|
||||
x_shift, y_shift = [
|
||||
(2 * random.random() - 1) * max_val
|
||||
for max_val in [
|
||||
|
@ -2606,7 +2626,7 @@ class ShowTwoPopulations(Scene):
|
|||
return result
|
||||
|
||||
def get_fox(self):
|
||||
return self.get_animal("fox", "#DF7F20")
|
||||
return self.get_animal("fox", FOX_COLOR)
|
||||
|
||||
def get_rabbit(self):
|
||||
return self.get_animal("rabbit", WHITE)
|
||||
|
@ -2627,30 +2647,280 @@ class ShowTwoPopulations(Scene):
|
|||
return count
|
||||
|
||||
|
||||
class PhaseSpaceOfPopulationModel(ShowTwoPopulations):
|
||||
class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene):
|
||||
CONFIG = {
|
||||
"origin": 5 * LEFT + 2.5 * DOWN,
|
||||
"vector_field_config": {
|
||||
"max_magnitude": 50,
|
||||
},
|
||||
"pi_creatures_start_on_screen": False,
|
||||
"default_pi_creature_kwargs": {
|
||||
"height": 1.8
|
||||
},
|
||||
"flow_time": 10,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.add_population_size_labels()
|
||||
self.add_axes()
|
||||
self.add_example_point()
|
||||
self.write_differential_equations()
|
||||
self.add_vectors()
|
||||
self.show_evolution_of_one_point()
|
||||
self.show_phase_flow()
|
||||
|
||||
def add_population_size_labels(self):
|
||||
pass
|
||||
|
||||
def add_axes(self):
|
||||
pass
|
||||
axes = self.axes = Axes(
|
||||
x_min=0,
|
||||
x_max=55,
|
||||
x_axis_config={"unit_size": 0.15},
|
||||
y_min=0,
|
||||
y_max=55,
|
||||
y_axis_config={"unit_size": 0.09},
|
||||
number_line_config={
|
||||
"tick_frequency": 10,
|
||||
},
|
||||
)
|
||||
axes.shift(self.origin)
|
||||
for axis in axes.x_axis, axes.y_axis:
|
||||
axis.add_numbers(*range(10, 60, 10))
|
||||
|
||||
axes_labels = self.axes_labels = VGroup(*[
|
||||
VGroup(
|
||||
method().scale_to_fit_height(0.75),
|
||||
TextMobject("Population"),
|
||||
).arrange_submobjects(RIGHT, buff=MED_SMALL_BUFF)
|
||||
for method in self.get_rabbit, self.get_fox
|
||||
])
|
||||
for axis, label, vect in zip(axes, axes_labels, [RIGHT, UP]):
|
||||
label.next_to(
|
||||
axis.main_line, vect,
|
||||
submobject_to_align=label[0]
|
||||
)
|
||||
|
||||
self.add(axes, axes_labels)
|
||||
|
||||
def add_example_point(self):
|
||||
pass
|
||||
axes = self.axes
|
||||
origin = self.origin
|
||||
x = self.start_num_rabbits
|
||||
y = self.start_num_foxes
|
||||
point = axes.coords_to_point(x, y)
|
||||
x_point = axes.coords_to_point(x, 0)
|
||||
y_point = axes.coords_to_point(0, y)
|
||||
v_line = DashedLine(x_point, point)
|
||||
h_line = DashedLine(y_point, point)
|
||||
v_line.set_color(FOX_COLOR)
|
||||
h_line.set_color(LIGHT_GREY)
|
||||
dot = Dot(point)
|
||||
|
||||
coord_pair = TexMobject(
|
||||
"(10, 10)", substrings_to_isolate=["10"]
|
||||
)
|
||||
pop_sizes = VGroup(Integer(10), Integer(10))
|
||||
pop_sizes[0].set_color(LIGHT_GREY)
|
||||
pop_sizes[1].set_color(FOX_COLOR)
|
||||
tens = coord_pair.get_parts_by_tex("10")
|
||||
tens.fade(1)
|
||||
|
||||
def get_pop_size_update(i):
|
||||
return ContinualChangingDecimal(
|
||||
pop_sizes[i],
|
||||
lambda a: int(np.round(
|
||||
axes.point_to_coords(dot.get_center())[i]
|
||||
)),
|
||||
position_update_func=lambda m: m.move_to(tens[i])
|
||||
)
|
||||
coord_pair.add_background_rectangle()
|
||||
coord_pair_update = ContinualUpdateFromFunc(
|
||||
coord_pair, lambda m: m.next_to(dot, UR, SMALL_BUFF)
|
||||
)
|
||||
pop_sizes_updates = [get_pop_size_update(i) for i in 0, 1]
|
||||
|
||||
phase_space = TextMobject("``Phase space''")
|
||||
phase_space.set_color(YELLOW)
|
||||
phase_space.scale(1.5)
|
||||
phase_space.to_edge(UP)
|
||||
phase_space.shift(2 * RIGHT)
|
||||
|
||||
self.play(ShowCreation(v_line))
|
||||
self.play(ShowCreation(h_line))
|
||||
dot.save_state()
|
||||
dot.move_to(origin)
|
||||
self.add(coord_pair_update)
|
||||
self.add(*pop_sizes_updates)
|
||||
self.play(
|
||||
dot.restore,
|
||||
UpdateFromAlphaFunc(pop_sizes, lambda m, a: m.set_fill(opacity=a)),
|
||||
VFadeIn(coord_pair)
|
||||
)
|
||||
self.wait()
|
||||
self.play(Write(phase_space))
|
||||
self.wait(2)
|
||||
self.play(FadeOut(VGroup(h_line, v_line, phase_space)))
|
||||
self.play(Rotating(
|
||||
dot,
|
||||
about_point=axes.coords_to_point(30, 30),
|
||||
rate_func=smooth,
|
||||
))
|
||||
|
||||
self.dot = dot
|
||||
self.coord_pair = coord_pair
|
||||
self.coord_pair_update = coord_pair_update
|
||||
self.pop_sizes = pop_sizes
|
||||
self.pop_sizes_updates = pop_sizes_updates
|
||||
|
||||
def write_differential_equations(self):
|
||||
variables = ["XX", "YY"]
|
||||
equations = TexMobject(
|
||||
"""
|
||||
{dXX \\over dt} =
|
||||
30 \\cdot XX - XX \\cdot YY \\\\
|
||||
\\quad \\\\
|
||||
{dYY \\over dt} =
|
||||
XX \\cdot YY - 30 \\cdot YY
|
||||
""",
|
||||
substrings_to_isolate=variables
|
||||
)
|
||||
animals = [self.get_fox().flip(), self.get_rabbit()]
|
||||
for char, animal in zip(variables, animals):
|
||||
for part in equations.get_parts_by_tex(char):
|
||||
animal_copy = animal.copy()
|
||||
animal_copy.scale_to_fit_height(0.5)
|
||||
animal_copy.move_to(part, DL)
|
||||
Transform(part, animal_copy).update(1)
|
||||
|
||||
equations.shift(2 * DOWN)
|
||||
rect = SurroundingRectangle(equations, color=YELLOW)
|
||||
rect.set_fill(BLACK, 0.8)
|
||||
title = TextMobject("Differential equations")
|
||||
title.next_to(rect, UP)
|
||||
title.set_color(rect.get_stroke_color())
|
||||
self.differential_equation_group = VGroup(
|
||||
rect, equations, title
|
||||
)
|
||||
self.differential_equation_group.to_corner(UR)
|
||||
|
||||
randy = self.pi_creature
|
||||
randy.next_to(rect, DL)
|
||||
|
||||
self.play(
|
||||
Write(title, run_time=1),
|
||||
ShowCreation(rect)
|
||||
)
|
||||
self.play(
|
||||
LaggedStart(FadeIn, equations),
|
||||
randy.change, "confused", equations,
|
||||
VFadeIn(randy),
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
def add_vectors(self):
|
||||
pass
|
||||
origin = self.axes.coords_to_point(0, 0)
|
||||
dot = self.dot
|
||||
randy = self.pi_creature
|
||||
|
||||
def show_evolution_of_one_point(self):
|
||||
pass
|
||||
def rescaled_field(point):
|
||||
x, y = self.axes.point_to_coords(point)
|
||||
result = preditor_prey_vector_field(np.array([x, y, 0]))
|
||||
return self.axes.coords_to_point(*result[:2]) - origin
|
||||
|
||||
self.vector_field_config.update({
|
||||
"x_min": origin[0] + 0.5,
|
||||
"x_max": self.axes.get_right()[0] + 1,
|
||||
"y_min": origin[1] + 0.5,
|
||||
"y_max": self.axes.get_top()[1],
|
||||
})
|
||||
vector_field = VectorField(
|
||||
rescaled_field, **self.vector_field_config
|
||||
)
|
||||
|
||||
def get_dot_vector():
|
||||
vector = vector_field.get_vector(dot.get_center())
|
||||
vector.scale(1, about_point=vector.get_start())
|
||||
return vector
|
||||
|
||||
dot_vector = get_dot_vector()
|
||||
|
||||
self.play(
|
||||
LaggedStart(GrowArrow, vector_field),
|
||||
randy.change, "thinking", dot,
|
||||
Animation(self.differential_equation_group)
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(
|
||||
Animation(dot),
|
||||
vector_field.set_fill, {"opacity": 0.2},
|
||||
GrowArrow(dot_vector),
|
||||
randy.change, "pondering",
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
dot.move_to, dot_vector.get_end(),
|
||||
dot.align_to, dot, RIGHT,
|
||||
run_time=3,
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
dot.move_to, dot_vector.get_end(),
|
||||
run_time=3,
|
||||
)
|
||||
self.wait(2)
|
||||
for x in range(6):
|
||||
new_dot_vector = get_dot_vector()
|
||||
fade_anims = [
|
||||
FadeOut(dot_vector),
|
||||
FadeIn(new_dot_vector),
|
||||
Animation(dot),
|
||||
]
|
||||
if x == 4:
|
||||
fade_anims += [
|
||||
vector_field.set_fill, {"opacity": 0.5},
|
||||
FadeOut(randy),
|
||||
FadeOut(self.differential_equation_group),
|
||||
]
|
||||
self.play(*fade_anims)
|
||||
dot_vector = new_dot_vector
|
||||
self.play(dot.move_to, dot_vector.get_end())
|
||||
|
||||
dot_movement = VectorFieldFlow(
|
||||
dot, lambda p: 0.3 * vector_field.func(p)
|
||||
)
|
||||
self.add(dot_movement)
|
||||
self.play(FadeOut(dot_vector))
|
||||
self.wait(10)
|
||||
self.play(
|
||||
vector_field.set_fill, {"opacity": 1.0},
|
||||
VFadeOut(dot),
|
||||
VFadeOut(self.coord_pair),
|
||||
UpdateFromAlphaFunc(self.pop_sizes, lambda m, a: m.set_fill(opacity=1 - a)),
|
||||
)
|
||||
self.remove(
|
||||
dot_movement,
|
||||
self.coord_pair_update,
|
||||
*self.pop_sizes_updates
|
||||
)
|
||||
self.wait()
|
||||
|
||||
self.vector_field = vector_field
|
||||
|
||||
def show_phase_flow(self):
|
||||
pass
|
||||
|
||||
vector_field = self.vector_field
|
||||
stream_lines = StreamLines(
|
||||
vector_field.func,
|
||||
start_points_generator_config={
|
||||
"x_min": vector_field.x_min,
|
||||
"x_max": vector_field.x_max,
|
||||
"y_min": vector_field.y_min,
|
||||
"y_max": vector_field.y_max,
|
||||
"delta_x": 0.25,
|
||||
"delta_y": 0.25,
|
||||
},
|
||||
min_magnitude=vector_field.min_magnitude,
|
||||
max_magnitude=vector_field.max_magnitude,
|
||||
virtual_time=4,
|
||||
)
|
||||
stream_line_animation = StreamLineAnimation(
|
||||
stream_lines,
|
||||
)
|
||||
self.add(stream_line_animation)
|
||||
self.add_foreground_mobjects(vector_field)
|
||||
self.wait(self.flow_time)
|
||||
|
|
Loading…
Add table
Reference in a new issue