mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
commit
9e1b6864f1
2 changed files with 290 additions and 137 deletions
|
@ -452,66 +452,6 @@ def make_alpha_winder(func, start, end, num_checkpoints):
|
||||||
return resit_near(func(x), check_points[index])
|
return resit_near(func(x), check_points[index])
|
||||||
return return_func
|
return return_func
|
||||||
|
|
||||||
def split_interval((a, b)):
|
|
||||||
mid = (a + b)/2.0
|
|
||||||
return ((a, mid), (mid, b))
|
|
||||||
|
|
||||||
# I am surely reinventing some wheel here, but what's done is done...
|
|
||||||
class RectangleData():
|
|
||||||
def __init__(self, x_interval, y_interval):
|
|
||||||
self.rect = (x_interval, y_interval)
|
|
||||||
|
|
||||||
def get_top_left(self):
|
|
||||||
return np.array((self.rect[0][0], self.rect[1][1]))
|
|
||||||
|
|
||||||
def get_top_right(self):
|
|
||||||
return np.array((self.rect[0][1], self.rect[1][1]))
|
|
||||||
|
|
||||||
def get_bottom_right(self):
|
|
||||||
return np.array((self.rect[0][1], self.rect[1][0]))
|
|
||||||
|
|
||||||
def get_bottom_left(self):
|
|
||||||
return np.array((self.rect[0][0], self.rect[1][0]))
|
|
||||||
|
|
||||||
def get_top(self):
|
|
||||||
return (self.get_top_left(), self.get_top_right())
|
|
||||||
|
|
||||||
def get_right(self):
|
|
||||||
return (self.get_top_right(), self.get_bottom_right())
|
|
||||||
|
|
||||||
def get_bottom(self):
|
|
||||||
return (self.get_bottom_right(), self.get_bottom_left())
|
|
||||||
|
|
||||||
def get_left(self):
|
|
||||||
return (self.get_bottom_left(), self.get_top_left())
|
|
||||||
|
|
||||||
def splits_on_dim(self, dim):
|
|
||||||
x_interval = self.rect[0]
|
|
||||||
y_interval = self.rect[1]
|
|
||||||
|
|
||||||
# TODO: Can refactor the following; will do later
|
|
||||||
if dim == 0:
|
|
||||||
return_data = [RectangleData(new_interval, y_interval) for new_interval in split_interval(x_interval)]
|
|
||||||
elif dim == 1:
|
|
||||||
return_data = [RectangleData(x_interval, new_interval) for new_interval in split_interval(y_interval)[::-1]]
|
|
||||||
else:
|
|
||||||
print "RectangleData.splits_on_dim passed illegitimate dimension!"
|
|
||||||
|
|
||||||
return tuple(return_data)
|
|
||||||
|
|
||||||
def split_line_on_dim(self, dim):
|
|
||||||
x_interval = self.rect[0]
|
|
||||||
y_interval = self.rect[1]
|
|
||||||
|
|
||||||
if dim == 0:
|
|
||||||
sides = (self.get_top(), self.get_bottom())
|
|
||||||
elif dim == 1:
|
|
||||||
sides = (self.get_left(), self.get_right())
|
|
||||||
else:
|
|
||||||
print "RectangleData.split_line_on_dim passed illegitimate dimension!"
|
|
||||||
|
|
||||||
return tuple([mid(x, y) for (x, y) in sides])
|
|
||||||
|
|
||||||
# The various inconsistent choices of what datatype to use where are a bit of a mess,
|
# The various inconsistent choices of what datatype to use where are a bit of a mess,
|
||||||
# but I'm more keen to rush this video out now than to sort this out.
|
# but I'm more keen to rush this video out now than to sort this out.
|
||||||
|
|
||||||
|
@ -645,6 +585,7 @@ def walker_animation_with_display(
|
||||||
number_update_func = None,
|
number_update_func = None,
|
||||||
show_arrows = True,
|
show_arrows = True,
|
||||||
scale_arrows = False,
|
scale_arrows = False,
|
||||||
|
num_decimal_points = 1,
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
|
|
||||||
|
@ -659,7 +600,7 @@ def walker_animation_with_display(
|
||||||
|
|
||||||
if number_update_func != None:
|
if number_update_func != None:
|
||||||
display = DecimalNumber(0,
|
display = DecimalNumber(0,
|
||||||
num_decimal_points = 1,
|
num_decimal_points = num_decimal_points,
|
||||||
fill_color = WHITE,
|
fill_color = WHITE,
|
||||||
include_background_rectangle = True)
|
include_background_rectangle = True)
|
||||||
display.background_rectangle.fill_opacity = 0.5
|
display.background_rectangle.fill_opacity = 0.5
|
||||||
|
@ -799,12 +740,14 @@ class PiWalker(ColorMappedByFuncScene):
|
||||||
"step_run_time" : 1,
|
"step_run_time" : 1,
|
||||||
"scale_arrows" : False,
|
"scale_arrows" : False,
|
||||||
"display_wind" : True,
|
"display_wind" : True,
|
||||||
|
"wind_reset_indices" : [],
|
||||||
"display_size" : False,
|
"display_size" : False,
|
||||||
"display_odometer" : False,
|
"display_odometer" : False,
|
||||||
"color_foreground_not_background" : False,
|
"color_foreground_not_background" : False,
|
||||||
"show_num_plane" : False,
|
"show_num_plane" : False,
|
||||||
"draw_lines" : True,
|
"draw_lines" : True,
|
||||||
"num_checkpoints" : 10,
|
"num_checkpoints" : 10,
|
||||||
|
"num_decimal_points" : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
@ -840,7 +783,7 @@ class PiWalker(ColorMappedByFuncScene):
|
||||||
clockwise_val_func = lambda p : -point_to_rev(self.func(p))
|
clockwise_val_func = lambda p : -point_to_rev(self.func(p))
|
||||||
alpha_winder = make_alpha_winder(clockwise_val_func, start_coords, end_coords, self.num_checkpoints)
|
alpha_winder = make_alpha_winder(clockwise_val_func, start_coords, end_coords, self.num_checkpoints)
|
||||||
number_update_func = lambda alpha, alpha_winder = alpha_winder, start_wind = start_wind: alpha_winder(alpha) - alpha_winder(0) + start_wind
|
number_update_func = lambda alpha, alpha_winder = alpha_winder, start_wind = start_wind: alpha_winder(alpha) - alpha_winder(0) + start_wind
|
||||||
start_wind = number_update_func(1)
|
start_wind = 0 if i + 1 in self.wind_reset_indices else number_update_func(1)
|
||||||
elif self.display_size:
|
elif self.display_size:
|
||||||
# We need to do this roundabout default argument thing to get the closure we want,
|
# We need to do this roundabout default argument thing to get the closure we want,
|
||||||
# so the next iteration changing val_alpha_func doesn't change this closure
|
# so the next iteration changing val_alpha_func doesn't change this closure
|
||||||
|
@ -858,7 +801,8 @@ class PiWalker(ColorMappedByFuncScene):
|
||||||
scale_arrows = self.scale_arrows,
|
scale_arrows = self.scale_arrows,
|
||||||
number_update_func = number_update_func,
|
number_update_func = number_update_func,
|
||||||
run_time = self.step_run_time,
|
run_time = self.step_run_time,
|
||||||
walker_stroke_color = WALKER_LIGHT_COLOR if self.color_foreground_not_background else BLACK
|
walker_stroke_color = WALKER_LIGHT_COLOR if self.color_foreground_not_background else BLACK,
|
||||||
|
num_decimal_points = self.num_decimal_points,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.display_odometer:
|
if self.display_odometer:
|
||||||
|
@ -904,6 +848,9 @@ class PiWalkerRect(PiWalker):
|
||||||
"walk_height" : 2,
|
"walk_height" : 2,
|
||||||
"func" : plane_func_from_complex_func(lambda c: c**2),
|
"func" : plane_func_from_complex_func(lambda c: c**2),
|
||||||
"double_up" : False,
|
"double_up" : False,
|
||||||
|
|
||||||
|
# New default for the scenes using this:
|
||||||
|
"display_wind" : True
|
||||||
}
|
}
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
@ -929,6 +876,114 @@ class PiWalkerCircle(PiWalker):
|
||||||
self.walk_coords = [r * np.array((np.cos(i * TAU/N), np.sin(i * TAU/N))) for i in range(N)]
|
self.walk_coords = [r * np.array((np.cos(i * TAU/N), np.sin(i * TAU/N))) for i in range(N)]
|
||||||
PiWalker.setup(self)
|
PiWalker.setup(self)
|
||||||
|
|
||||||
|
def split_interval((a, b)):
|
||||||
|
mid = (a + b)/2.0
|
||||||
|
return ((a, mid), (mid, b))
|
||||||
|
|
||||||
|
# I am surely reinventing some wheel here, but what's done is done...
|
||||||
|
class RectangleData():
|
||||||
|
def __init__(self, x_interval, y_interval):
|
||||||
|
self.rect = (x_interval, y_interval)
|
||||||
|
|
||||||
|
def get_top_left(self):
|
||||||
|
return np.array((self.rect[0][0], self.rect[1][1]))
|
||||||
|
|
||||||
|
def get_top_right(self):
|
||||||
|
return np.array((self.rect[0][1], self.rect[1][1]))
|
||||||
|
|
||||||
|
def get_bottom_right(self):
|
||||||
|
return np.array((self.rect[0][1], self.rect[1][0]))
|
||||||
|
|
||||||
|
def get_bottom_left(self):
|
||||||
|
return np.array((self.rect[0][0], self.rect[1][0]))
|
||||||
|
|
||||||
|
def get_top(self):
|
||||||
|
return (self.get_top_left(), self.get_top_right())
|
||||||
|
|
||||||
|
def get_right(self):
|
||||||
|
return (self.get_top_right(), self.get_bottom_right())
|
||||||
|
|
||||||
|
def get_bottom(self):
|
||||||
|
return (self.get_bottom_right(), self.get_bottom_left())
|
||||||
|
|
||||||
|
def get_left(self):
|
||||||
|
return (self.get_bottom_left(), self.get_top_left())
|
||||||
|
|
||||||
|
def get_center(self):
|
||||||
|
return interpolate(self.get_top_left(), self.get_bottom_right(), 0.5)
|
||||||
|
|
||||||
|
def get_width(self):
|
||||||
|
return self.rect[0][1] - self.rect[0][0]
|
||||||
|
|
||||||
|
def get_height(self):
|
||||||
|
return self.rect[1][1] - self.rect[1][0]
|
||||||
|
|
||||||
|
def splits_on_dim(self, dim):
|
||||||
|
x_interval = self.rect[0]
|
||||||
|
y_interval = self.rect[1]
|
||||||
|
|
||||||
|
# TODO: Can refactor the following; will do later
|
||||||
|
if dim == 0:
|
||||||
|
return_data = [RectangleData(new_interval, y_interval) for new_interval in split_interval(x_interval)]
|
||||||
|
elif dim == 1:
|
||||||
|
return_data = [RectangleData(x_interval, new_interval) for new_interval in split_interval(y_interval)[::-1]]
|
||||||
|
else:
|
||||||
|
print "RectangleData.splits_on_dim passed illegitimate dimension!"
|
||||||
|
|
||||||
|
return tuple(return_data)
|
||||||
|
|
||||||
|
def split_line_on_dim(self, dim):
|
||||||
|
x_interval = self.rect[0]
|
||||||
|
y_interval = self.rect[1]
|
||||||
|
|
||||||
|
if dim == 0:
|
||||||
|
sides = (self.get_top(), self.get_bottom())
|
||||||
|
elif dim == 1:
|
||||||
|
sides = (self.get_left(), self.get_right())
|
||||||
|
else:
|
||||||
|
print "RectangleData.split_line_on_dim passed illegitimate dimension!"
|
||||||
|
|
||||||
|
return tuple([mid(x, y) for (x, y) in sides])
|
||||||
|
|
||||||
|
|
||||||
|
class EquationSolver2dNode(object):
|
||||||
|
def __init__(self, first_anim, children = []):
|
||||||
|
self.first_anim = first_anim
|
||||||
|
self.children = children
|
||||||
|
|
||||||
|
def depth(self):
|
||||||
|
if len(self.children) == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return 1 + max(map(lambda n : n.depth(), self.children))
|
||||||
|
|
||||||
|
def nodes_at_depth(self, n):
|
||||||
|
if n == 0:
|
||||||
|
return [self]
|
||||||
|
|
||||||
|
# Not the efficient way to flatten lists, because Python + is linear in list size,
|
||||||
|
# but we have at most two children, so no big deal here
|
||||||
|
return sum(map(lambda c : c.nodes_at_depth(n - 1), self.children), [])
|
||||||
|
|
||||||
|
# This is definitely NOT the efficient way to do BFS, but I'm just trying to write something
|
||||||
|
# quick without thinking that gets the job done on small instances for now
|
||||||
|
def hacky_bfs(self):
|
||||||
|
depth = self.depth()
|
||||||
|
|
||||||
|
# Not the efficient way to flatten lists, because Python + is linear in list size,
|
||||||
|
# but this IS hacky_bfs...
|
||||||
|
return sum(map(lambda i : self.nodes_at_depth(i), range(depth + 1)), [])
|
||||||
|
|
||||||
|
def display_in_series(self):
|
||||||
|
return Succession(self.first_anim, *map(lambda n : n.display_in_series(), self.children))
|
||||||
|
|
||||||
|
def display_in_parallel(self):
|
||||||
|
return Succession(self.first_anim, AnimationGroup(*map(lambda n : n.display_in_parallel(), self.children)))
|
||||||
|
|
||||||
|
def display_in_bfs(self):
|
||||||
|
bfs_nodes = self.hacky_bfs()
|
||||||
|
return Succession(*map(lambda n : n.first_anim, bfs_nodes))
|
||||||
|
|
||||||
class EquationSolver2d(ColorMappedObjectsScene):
|
class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"camera_config" : {"use_z_coordinate_for_display_order": True},
|
"camera_config" : {"use_z_coordinate_for_display_order": True},
|
||||||
|
@ -938,7 +993,11 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
"initial_upper_y" : 3,
|
"initial_upper_y" : 3,
|
||||||
"num_iterations" : 1,
|
"num_iterations" : 1,
|
||||||
"num_checkpoints" : 10,
|
"num_checkpoints" : 10,
|
||||||
|
|
||||||
|
# Should really merge this into one enum-style variable
|
||||||
"display_in_parallel" : False,
|
"display_in_parallel" : False,
|
||||||
|
"display_in_bfs" : False,
|
||||||
|
|
||||||
"use_fancy_lines" : True,
|
"use_fancy_lines" : True,
|
||||||
# TODO: Consider adding a "find_all_roots" flag, which could be turned off
|
# TODO: Consider adding a "find_all_roots" flag, which could be turned off
|
||||||
# to only explore one of the two candidate subrectangles when both are viable
|
# to only explore one of the two candidate subrectangles when both are viable
|
||||||
|
@ -955,7 +1014,11 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
"show_winding_numbers" : True,
|
"show_winding_numbers" : True,
|
||||||
|
|
||||||
# Used for UhOhScene;
|
# Used for UhOhScene;
|
||||||
"manual_wind_override" : None
|
"manual_wind_override" : None,
|
||||||
|
|
||||||
|
"show_cursor" : False,
|
||||||
|
|
||||||
|
"linger_parameter" : 0.2,
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
@ -976,11 +1039,14 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
obj1.color_using_background_image(bg)
|
obj1.color_using_background_image(bg)
|
||||||
|
|
||||||
run_time_base = 1
|
run_time_base = 1
|
||||||
run_time_with_lingering = run_time_base + 0.2
|
run_time_with_lingering = run_time_base + self.linger_parameter
|
||||||
base_rate = lambda t : t
|
base_rate = lambda t : t
|
||||||
linger_rate = squish_rate_func(lambda t : t, 0,
|
linger_rate = squish_rate_func(lambda t : t, 0,
|
||||||
fdiv(run_time_base, run_time_with_lingering))
|
fdiv(run_time_base, run_time_with_lingering))
|
||||||
|
|
||||||
|
cursor_base = TextMobject("?")
|
||||||
|
cursor_base.scale(2)
|
||||||
|
|
||||||
# Helper functions for manual_wind_override
|
# Helper functions for manual_wind_override
|
||||||
def head(m):
|
def head(m):
|
||||||
if m == None:
|
if m == None:
|
||||||
|
@ -998,7 +1064,7 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
print "Solver at depth: " + str(cur_depth)
|
print "Solver at depth: " + str(cur_depth)
|
||||||
|
|
||||||
if cur_depth >= self.num_iterations:
|
if cur_depth >= self.num_iterations:
|
||||||
return empty_animation
|
return EquationSolver2dNode(empty_animation)
|
||||||
|
|
||||||
def draw_line_return_wind(start, end, start_wind, should_linger = False, draw_line = True):
|
def draw_line_return_wind(start, end, start_wind, should_linger = False, draw_line = True):
|
||||||
alpha_winder = make_alpha_winder(clockwise_val_func, start, end, self.num_checkpoints)
|
alpha_winder = make_alpha_winder(clockwise_val_func, start, end, self.num_checkpoints)
|
||||||
|
@ -1050,6 +1116,24 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
draw_line = i in sides_to_draw)
|
draw_line = i in sides_to_draw)
|
||||||
anim = Succession(anim, next_anim)
|
anim = Succession(anim, next_anim)
|
||||||
|
|
||||||
|
if self.show_cursor:
|
||||||
|
cursor = cursor_base.copy()
|
||||||
|
center_x, center_y = rect.get_center()
|
||||||
|
width = rect.get_width()
|
||||||
|
height = rect.get_height()
|
||||||
|
|
||||||
|
cursor.move_to(num_plane.coords_to_point(center_x, center_y))
|
||||||
|
cursor.scale(min(width, height))
|
||||||
|
|
||||||
|
# Do a quick FadeIn, wait, and quick FadeOut on the cursor, matching rectangle-drawing time
|
||||||
|
cursor_anim = Succession(
|
||||||
|
FadeIn(cursor, run_time = 0.1),
|
||||||
|
Animation(cursor, run_time = 3.8),
|
||||||
|
FadeOut(cursor, run_time = 0.1)
|
||||||
|
)
|
||||||
|
|
||||||
|
anim = AnimationGroup(anim, cursor_anim)
|
||||||
|
|
||||||
total_wind = head(manual_wind_override) or round(wind_so_far)
|
total_wind = head(manual_wind_override) or round(wind_so_far)
|
||||||
|
|
||||||
if total_wind == 0:
|
if total_wind == 0:
|
||||||
|
@ -1064,14 +1148,14 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
# their "Nothing here" status?
|
# their "Nothing here" status?
|
||||||
# Or draw a large X or something
|
# Or draw a large X or something
|
||||||
fill_rect = polygonObject = Polygon(*points, fill_opacity = 0.8, color = DARK_GREY)
|
fill_rect = polygonObject = Polygon(*points, fill_opacity = 0.8, color = DARK_GREY)
|
||||||
return Succession(anim, FadeIn(fill_rect))
|
return EquationSolver2dNode(Succession(anim, FadeIn(fill_rect)))
|
||||||
else:
|
else:
|
||||||
(sub_rect1, sub_rect2) = rect.splits_on_dim(dim_to_split)
|
(sub_rect1, sub_rect2) = rect.splits_on_dim(dim_to_split)
|
||||||
if dim_to_split == 0:
|
if dim_to_split == 0:
|
||||||
sub_rect_and_sides = [(sub_rect1, 1), (sub_rect2, 3)]
|
sub_rect_and_sides = [(sub_rect1, 1), (sub_rect2, 3)]
|
||||||
else:
|
else:
|
||||||
sub_rect_and_sides = [(sub_rect1, 2), (sub_rect2, 0)]
|
sub_rect_and_sides = [(sub_rect1, 2), (sub_rect2, 0)]
|
||||||
sub_anims = [
|
children = [
|
||||||
Animate2dSolver(
|
Animate2dSolver(
|
||||||
cur_depth = cur_depth + 1,
|
cur_depth = cur_depth + 1,
|
||||||
rect = sub_rect,
|
rect = sub_rect,
|
||||||
|
@ -1084,14 +1168,8 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
mid_line_coords = rect.split_line_on_dim(dim_to_split)
|
mid_line_coords = rect.split_line_on_dim(dim_to_split)
|
||||||
mid_line_points = [num_plane.coords_to_point(x, y) + 2 * IN for (x, y) in mid_line_coords]
|
mid_line_points = [num_plane.coords_to_point(x, y) + 2 * IN for (x, y) in mid_line_coords]
|
||||||
mid_line = DashedLine(*mid_line_points)
|
mid_line = DashedLine(*mid_line_points)
|
||||||
if self.display_in_parallel:
|
|
||||||
recursive_anim = AnimationGroup(*sub_anims)
|
return EquationSolver2dNode(Succession(anim, ShowCreation(mid_line)), children)
|
||||||
else:
|
|
||||||
recursive_anim = Succession(*sub_anims)
|
|
||||||
return Succession(anim,
|
|
||||||
ShowCreation(mid_line),
|
|
||||||
recursive_anim
|
|
||||||
)
|
|
||||||
|
|
||||||
lower_x = self.initial_lower_x
|
lower_x = self.initial_lower_x
|
||||||
upper_x = self.initial_upper_x
|
upper_x = self.initial_upper_x
|
||||||
|
@ -1105,7 +1183,7 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
|
|
||||||
print "Starting to compute anim"
|
print "Starting to compute anim"
|
||||||
|
|
||||||
anim = self.anim = Animate2dSolver(
|
node = Animate2dSolver(
|
||||||
cur_depth = 0,
|
cur_depth = 0,
|
||||||
rect = rect,
|
rect = rect,
|
||||||
dim_to_split = 0,
|
dim_to_split = 0,
|
||||||
|
@ -1115,6 +1193,13 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
|
|
||||||
print "Done computing anim"
|
print "Done computing anim"
|
||||||
|
|
||||||
|
if self.display_in_parallel:
|
||||||
|
anim = node.display_in_parallel()
|
||||||
|
elif self.display_in_bfs:
|
||||||
|
anim = node.display_in_bfs()
|
||||||
|
else:
|
||||||
|
anim = node.display_in_series()
|
||||||
|
|
||||||
# Keep timing details here in sync with details above
|
# Keep timing details here in sync with details above
|
||||||
rect_points = [
|
rect_points = [
|
||||||
rect.get_top_left(),
|
rect.get_top_left(),
|
||||||
|
@ -1146,6 +1231,11 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||||
|
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
|
# In case we wish to reference these later, as had been done in the
|
||||||
|
# WindingNumber_G/SearchSpacePerimeterVsArea scene at one point
|
||||||
|
self.node = node
|
||||||
|
self.anim = anim
|
||||||
|
|
||||||
# TODO: Perhaps have option for bullets (pulses) to fade out and in at ends of line, instead of
|
# TODO: Perhaps have option for bullets (pulses) to fade out and in at ends of line, instead of
|
||||||
# jarringly popping out and in?
|
# jarringly popping out and in?
|
||||||
#
|
#
|
||||||
|
@ -1763,10 +1853,12 @@ class DemonstrateColorMapping(ColorMappedObjectsScene):
|
||||||
self.play(ReplacementTransform(background, bright_background))
|
self.play(ReplacementTransform(background, bright_background))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
|
# Everything in this is manually kept in sync with WindingNumber_G/TransitionFromPathsToBoundaries
|
||||||
class LoopSplitScene(ColorMappedObjectsScene):
|
class LoopSplitScene(ColorMappedObjectsScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
# TODO: Change this to something more colorful down the midline
|
"func" : plane_func_by_wind_spec(
|
||||||
"func" : plane_func_by_wind_spec((0.1/2, 1.1/2, 1), (-3.1/2, -1.3/2, 2), (1.8/2, -2.1/2, -1)),
|
(-2, 0, 2), (2, 0, 1)
|
||||||
|
),
|
||||||
"use_fancy_lines" : True,
|
"use_fancy_lines" : True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1776,7 +1868,7 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||||
num_bullets = 4,
|
num_bullets = 4,
|
||||||
pulse_time = 1,
|
pulse_time = 1,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
line = Line(start, end, color = WHITE, **kwargs)
|
line = Line(start, end, color = WHITE, stroke_width = 4, **kwargs)
|
||||||
if self.use_fancy_lines:
|
if self.use_fancy_lines:
|
||||||
line.color_using_background_image(self.background_image_file)
|
line.color_using_background_image(self.background_image_file)
|
||||||
anim = LinePulser(
|
anim = LinePulser(
|
||||||
|
@ -1815,35 +1907,24 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||||
def pl(a, b):
|
def pl(a, b):
|
||||||
return self.PulsedLine(a, b, default_bullet)
|
return self.PulsedLine(a, b, default_bullet)
|
||||||
|
|
||||||
faded = 0.3
|
def indicate_circle(x, double_horizontal_stretch = False):
|
||||||
|
circle = Circle(color = WHITE, radius = 2 * np.sqrt(2))
|
||||||
|
circle.move_to(x.get_center())
|
||||||
|
|
||||||
# Workaround for FadeOut/FadeIn not playing well with ContinualAnimations due to
|
if x.get_slope() == 0:
|
||||||
# Transforms making copies no longer identified with the ContinualAnimation's tracked mobject
|
circle.stretch(0.2, 1)
|
||||||
def fader_bullet(start, end, mob):
|
if double_horizontal_stretch:
|
||||||
return UpdateFromAlphaFunc(mob, lambda m, a : m.set_fill(opacity = interpolate(start, end, a)))
|
circle.stretch(2, 0)
|
||||||
|
else:
|
||||||
def fader_blist(start, end, blist):
|
circle.stretch(0.2, 0)
|
||||||
return map(lambda b : fader_bullet(start, end, b), blist)
|
|
||||||
|
|
||||||
def fader_widthmob(start, end, mob):
|
|
||||||
return UpdateFromAlphaFunc(mob, lambda m, a : m.set_stroke(width = interpolate(start, end, a) * stroke_width))
|
|
||||||
|
|
||||||
def indicate_circle(x):
|
|
||||||
circle = Circle(color = WHITE)
|
|
||||||
circle.surround(x)
|
|
||||||
# if x.get_slope == 0:
|
|
||||||
# circle.stretch(0, 0.3)
|
|
||||||
# else:
|
|
||||||
# circle.stretch(0.3, 0)
|
|
||||||
circle.stretch(0.3, 0.3)
|
|
||||||
return circle
|
return circle
|
||||||
|
|
||||||
tl_line_trip = pl(tl, tm)
|
tl_line_trip = pl(tl, tm)
|
||||||
left_mid_trip = pl(tm, bm)
|
midline_left_trip = pl(tm, bm)
|
||||||
bl_line_trip = pl(bm, bl)
|
bl_line_trip = pl(bm, bl)
|
||||||
left_line_trip = pl(bl, tl)
|
left_line_trip = pl(bl, tl)
|
||||||
|
|
||||||
left_square_trips = [tl_line_trip, left_mid_trip, bl_line_trip, left_line_trip]
|
left_square_trips = [tl_line_trip, midline_left_trip, bl_line_trip, left_line_trip]
|
||||||
left_square_lines = [x[0] for x in left_square_trips]
|
left_square_lines = [x[0] for x in left_square_trips]
|
||||||
left_square_lines_vmobject = VMobject(*left_square_lines)
|
left_square_lines_vmobject = VMobject(*left_square_lines)
|
||||||
left_square_bullets = [x[1] for x in left_square_trips]
|
left_square_bullets = [x[1] for x in left_square_trips]
|
||||||
|
@ -1852,54 +1933,93 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||||
tr_line_trip = pl(tm, tr)
|
tr_line_trip = pl(tm, tr)
|
||||||
right_line_trip = pl(tr, br)
|
right_line_trip = pl(tr, br)
|
||||||
br_line_trip = pl(br, bm)
|
br_line_trip = pl(br, bm)
|
||||||
right_midline_trip = pl(bm, tm)
|
midline_right_trip = pl(bm, tm)
|
||||||
|
|
||||||
right_square_trips = [tr_line_trip, right_line_trip, br_line_trip, right_midline_trip]
|
right_square_trips = [tr_line_trip, right_line_trip, br_line_trip, midline_right_trip]
|
||||||
right_square_lines = [x[0] for x in right_square_trips]
|
right_square_lines = [x[0] for x in right_square_trips]
|
||||||
right_square_lines_vmobject = VMobject(*right_square_lines)
|
right_square_lines_vmobject = VMobject(*right_square_lines)
|
||||||
right_square_bullets = [x[1] for x in right_square_trips]
|
right_square_bullets = [x[1] for x in right_square_trips]
|
||||||
right_square_anims = [x[2] for x in right_square_trips]
|
right_square_anims = [x[2] for x in right_square_trips]
|
||||||
|
|
||||||
# Hm... still some slight bug with this.
|
midline_trips = [midline_left_trip, midline_right_trip]
|
||||||
|
midline_lines = [x[0] for x in midline_trips]
|
||||||
|
midline_lines_vmobject = VMobject(*midline_lines)
|
||||||
|
midline_bullets = [x[1] for x in midline_trips]
|
||||||
|
midline_anims = [x[1] for x in midline_trips]
|
||||||
|
|
||||||
|
left_line = left_line_trip[0]
|
||||||
|
right_line = right_line_trip[0]
|
||||||
|
|
||||||
for b in left_square_bullets + right_square_bullets:
|
for b in left_square_bullets + right_square_bullets:
|
||||||
b.set_fill(opacity = 0)
|
b.set_fill(opacity = 0)
|
||||||
|
|
||||||
|
faded = 0.3
|
||||||
|
|
||||||
|
# Workaround for FadeOut/FadeIn not playing well with ContinualAnimations due to
|
||||||
|
# Transforms making copies no longer identified with the ContinualAnimation's tracked mobject
|
||||||
|
def bullet_fade(start, end, mob):
|
||||||
|
return UpdateFromAlphaFunc(mob, lambda m, a : m.set_fill(opacity = interpolate(start, end, a)))
|
||||||
|
|
||||||
|
def bullet_list_fade(start, end, bullet_list):
|
||||||
|
return map(lambda b : bullet_fade(start, end, b), bullet_list)
|
||||||
|
|
||||||
|
def line_fade(start, end, mob):
|
||||||
|
return UpdateFromAlphaFunc(mob, lambda m, a : m.set_stroke(width = interpolate(start, end, a) * stroke_width))
|
||||||
|
|
||||||
|
def play_combined_fade(start, end, lines_vmobject, bullets):
|
||||||
|
self.play(
|
||||||
|
line_fade(start, end, lines_vmobject),
|
||||||
|
*bullet_list_fade(start, end, bullets)
|
||||||
|
)
|
||||||
|
|
||||||
|
def play_fade_left(start, end):
|
||||||
|
play_combined_fade(start, end, left_square_lines_vmobject, left_square_bullets)
|
||||||
|
|
||||||
|
def play_fade_right(start, end):
|
||||||
|
play_combined_fade(start, end, right_square_lines_vmobject, right_square_bullets)
|
||||||
|
|
||||||
|
def play_fade_mid(start, end):
|
||||||
|
play_combined_fade(start, end, midline_lines_vmobject, midline_bullets)
|
||||||
|
|
||||||
|
def flash_circles(circles):
|
||||||
|
self.play(LaggedStart(FadeIn, VGroup(circles)))
|
||||||
|
self.play(*map(FadeOut, circles))
|
||||||
|
|
||||||
|
self.add(left_square_lines_vmobject, right_square_lines_vmobject)
|
||||||
|
self.remove(*midline_lines)
|
||||||
|
self.wait()
|
||||||
|
self.play(ShowCreation(midline_lines[0]))
|
||||||
|
self.add(midline_lines_vmobject)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
self.add(*left_square_anims)
|
self.add(*left_square_anims)
|
||||||
self.play(fader_widthmob(0, 1, left_square_lines_vmobject), *fader_blist(0, 1, left_square_bullets))
|
self.play(line_fade(1, faded, right_square_lines_vmobject), *bullet_list_fade(0, 1, left_square_bullets))
|
||||||
|
self.wait()
|
||||||
|
flash_circles([indicate_circle(l) for l in left_square_lines])
|
||||||
|
self.play(line_fade(faded, 1, right_square_lines_vmobject), *bullet_list_fade(1, 0, left_square_bullets))
|
||||||
|
|
||||||
self.add(*right_square_anims)
|
self.add(*right_square_anims)
|
||||||
self.play(fader_widthmob(0, 1, right_square_lines_vmobject), *fader_blist(0, 1, right_square_bullets))
|
self.play(line_fade(1, faded, left_square_lines_vmobject), *bullet_list_fade(0, 1, right_square_bullets))
|
||||||
|
|
||||||
self.play(fader_widthmob(1, faded, right_square_lines_vmobject), *fader_blist(1, faded, right_square_bullets))
|
|
||||||
|
|
||||||
# left_circlers = [indicate_circle(l) for l in left_square_lines]
|
|
||||||
# self.play(*map(FadeIn, left_circlers))
|
|
||||||
# self.play(*map(FadeOut, left_circlers))
|
|
||||||
|
|
||||||
self.play(ShowCreation(indicate_circle(left_square_lines[1])))
|
|
||||||
|
|
||||||
self.wait()
|
self.wait()
|
||||||
|
flash_circles([indicate_circle(l) for l in right_square_lines])
|
||||||
return
|
self.play(line_fade(faded, 1, left_square_lines_vmobject), *bullet_list_fade(1, 0, right_square_bullets))
|
||||||
|
|
||||||
highlight_circle = Circle(color = WHITE) # Perhaps make this a dashed circle?
|
|
||||||
highlight_circle.surround(mid_lines)
|
|
||||||
highlight_circle.stretch(0.3, 0)
|
|
||||||
self.play(Indicate(mid_lines), ShowCreation(highlight_circle, run_time = 0.5))
|
|
||||||
|
|
||||||
self.wait()
|
self.wait()
|
||||||
|
self.play(*bullet_list_fade(0, 1, left_square_bullets + right_square_bullets))
|
||||||
|
|
||||||
self.play(FadeOut(highlight_circle), FadeOut(mid_lines))
|
outside_circlers = [
|
||||||
# Because FadeOut didn't remove the continual pulsers, we remove them manually
|
indicate_circle(left_line),
|
||||||
self.remove(mid_line_left[1], mid_line_right[1])
|
indicate_circle(right_line),
|
||||||
|
indicate_circle(top_line, double_horizontal_stretch = True),
|
||||||
|
indicate_circle(bottom_line, double_horizontal_stretch = True)
|
||||||
|
]
|
||||||
|
flash_circles(outside_circlers)
|
||||||
|
|
||||||
# Brings loop back together; keep in sync with motions which bring loop apart above
|
inner_circle = indicate_circle(midline_lines[0])
|
||||||
# self.play(
|
self.play(FadeIn(inner_circle))
|
||||||
# ApplyMethod(left_open_loop.shift, 2 * RIGHT),
|
self.play(FadeOut(inner_circle), line_fade(1, 0, midline_lines_vmobject), *bullet_list_fade(1, 0, midline_bullets))
|
||||||
# ApplyMethod(right_open_loop.shift, 2 * LEFT)
|
|
||||||
# )
|
|
||||||
|
|
||||||
self.wait()
|
self.wait(3)
|
||||||
|
|
||||||
# Is there a way to abstract this into a general process to derive a new mapped scene from an old scene?
|
# Is there a way to abstract this into a general process to derive a new mapped scene from an old scene?
|
||||||
class LoopSplitSceneMapped(LoopSplitScene):
|
class LoopSplitSceneMapped(LoopSplitScene):
|
||||||
|
@ -1928,14 +2048,16 @@ class SolveX5MinusXMinus1(EquationSolver2d):
|
||||||
"use_fancy_lines" : True,
|
"use_fancy_lines" : True,
|
||||||
}
|
}
|
||||||
|
|
||||||
class SolveX5MinusXMinus1Parallel(EquationSolver2d):
|
class SolveX5MinusXMinus1Parallel(SolveX5MinusXMinus1):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : plane_func_from_complex_func(lambda c : c**5 - c - 1),
|
|
||||||
"num_iterations" : 5,
|
|
||||||
"use_fancy_lines" : True,
|
|
||||||
"display_in_parallel" : True
|
"display_in_parallel" : True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SolveX5MinusXMinus1BFS(SolveX5MinusXMinus1):
|
||||||
|
CONFIG = {
|
||||||
|
"display_in_bfs" : True
|
||||||
|
}
|
||||||
|
|
||||||
class PreviewClip(EquationSolver2d):
|
class PreviewClip(EquationSolver2d):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : example_plane_func,
|
"func" : example_plane_func,
|
||||||
|
@ -1944,6 +2066,23 @@ class PreviewClip(EquationSolver2d):
|
||||||
"use_fancy_lines" : True,
|
"use_fancy_lines" : True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class QuickPreview(PreviewClip):
|
||||||
|
CONFIG = {
|
||||||
|
"num_iterations" : 3,
|
||||||
|
"display_in_parallel" : False,
|
||||||
|
"display_in_bfs" : True,
|
||||||
|
"show_cursor" : True
|
||||||
|
}
|
||||||
|
|
||||||
|
class LongEquationSolver(EquationSolver2d):
|
||||||
|
CONFIG = {
|
||||||
|
"func" : example_plane_func,
|
||||||
|
"num_iterations" : 10,
|
||||||
|
"display_in_bfs" : True,
|
||||||
|
"linger_parameter" : 0.4,
|
||||||
|
"show_cursor" : True,
|
||||||
|
}
|
||||||
|
|
||||||
# TODO: Borsuk-Ulam visuals
|
# TODO: Borsuk-Ulam visuals
|
||||||
# Note: May want to do an ordinary square scene, then MappingCamera it into a circle
|
# Note: May want to do an ordinary square scene, then MappingCamera it into a circle
|
||||||
# class BorsukUlamScene(PiWalker):
|
# class BorsukUlamScene(PiWalker):
|
||||||
|
@ -2468,6 +2607,12 @@ class OneFifthTwoFifthWinder(SpecifiedWinder):
|
||||||
"step_size" : 0.01,
|
"step_size" : 0.01,
|
||||||
"show_num_plane" : False,
|
"show_num_plane" : False,
|
||||||
"step_run_time" : 6,
|
"step_run_time" : 6,
|
||||||
|
"num_decimal_points" : 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
class OneFifthOneFifthWinderWithReset(OneFifthTwoFifthWinder):
|
||||||
|
CONFIG = {
|
||||||
|
"wind_reset_indices" : [1]
|
||||||
}
|
}
|
||||||
|
|
||||||
class OneFifthTwoFifthWinderOdometer(OneFifthTwoFifthWinder):
|
class OneFifthTwoFifthWinderOdometer(OneFifthTwoFifthWinder):
|
||||||
|
@ -2500,6 +2645,8 @@ class CWColorWalk(PiWalkerRect):
|
||||||
"walk_width" : 2,
|
"walk_width" : 2,
|
||||||
"walk_height" : 2,
|
"walk_height" : 2,
|
||||||
"draw_lines" : False,
|
"draw_lines" : False,
|
||||||
|
"display_wind" : False,
|
||||||
|
"step_run_time" : 2
|
||||||
}
|
}
|
||||||
|
|
||||||
class CWColorWalkOdometer(CWColorWalk):
|
class CWColorWalkOdometer(CWColorWalk):
|
||||||
|
@ -2521,7 +2668,8 @@ class CCWColorWalkOdometer(CCWColorWalk):
|
||||||
class ThreeTurnWalker(PiWalkerRect):
|
class ThreeTurnWalker(PiWalkerRect):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : plane_func_from_complex_func(lambda c: c**3 * complex(1, 1)**3),
|
"func" : plane_func_from_complex_func(lambda c: c**3 * complex(1, 1)**3),
|
||||||
"double_up" : True
|
"double_up" : True,
|
||||||
|
"wind_reset_indices" : [4]
|
||||||
}
|
}
|
||||||
|
|
||||||
class ThreeTurnWalkerOdometer(ThreeTurnWalker):
|
class ThreeTurnWalkerOdometer(ThreeTurnWalker):
|
||||||
|
@ -2561,6 +2709,7 @@ class ZeroTurnWalkerOdometer(ZeroTurnWalker):
|
||||||
|
|
||||||
class NegOneTurnWalker(PiWalkerRect):
|
class NegOneTurnWalker(PiWalkerRect):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
"step_run_time" : 2,
|
||||||
"func" : plane_func_by_wind_spec((0, 0, -1))
|
"func" : plane_func_by_wind_spec((0, 0, -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,11 @@ class Circle(Arc):
|
||||||
def surround(self, mobject, dim_to_match = 0, stretch = False, buffer_factor = 1.2):
|
def surround(self, mobject, dim_to_match = 0, stretch = False, buffer_factor = 1.2):
|
||||||
# Ignores dim_to_match and stretch; result will always be a circle
|
# Ignores dim_to_match and stretch; result will always be a circle
|
||||||
# TODO: Perhaps create an ellipse class to handle singele-dimension stretching
|
# TODO: Perhaps create an ellipse class to handle singele-dimension stretching
|
||||||
|
|
||||||
|
# Something goes wrong here when surrounding lines?
|
||||||
|
# TODO: Figure out and fix
|
||||||
self.replace(mobject, dim_to_match, stretch)
|
self.replace(mobject, dim_to_match, stretch)
|
||||||
|
|
||||||
self.scale_to_fit_width(np.sqrt(mobject.get_width()**2 + mobject.get_height()**2))
|
self.scale_to_fit_width(np.sqrt(mobject.get_width()**2 + mobject.get_height()**2))
|
||||||
self.scale(buffer_factor)
|
self.scale(buffer_factor)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue