mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 08:54:38 +08:00
@ -452,66 +452,6 @@ def make_alpha_winder(func, start, end, num_checkpoints):
|
||||
return resit_near(func(x), check_points[index])
|
||||
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,
|
||||
# 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,
|
||||
show_arrows = True,
|
||||
scale_arrows = False,
|
||||
num_decimal_points = 1,
|
||||
**kwargs
|
||||
):
|
||||
|
||||
@ -659,7 +600,7 @@ def walker_animation_with_display(
|
||||
|
||||
if number_update_func != None:
|
||||
display = DecimalNumber(0,
|
||||
num_decimal_points = 1,
|
||||
num_decimal_points = num_decimal_points,
|
||||
fill_color = WHITE,
|
||||
include_background_rectangle = True)
|
||||
display.background_rectangle.fill_opacity = 0.5
|
||||
@ -799,12 +740,14 @@ class PiWalker(ColorMappedByFuncScene):
|
||||
"step_run_time" : 1,
|
||||
"scale_arrows" : False,
|
||||
"display_wind" : True,
|
||||
"wind_reset_indices" : [],
|
||||
"display_size" : False,
|
||||
"display_odometer" : False,
|
||||
"color_foreground_not_background" : False,
|
||||
"show_num_plane" : False,
|
||||
"draw_lines" : True,
|
||||
"num_checkpoints" : 10,
|
||||
"num_decimal_points" : 1
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
@ -840,7 +783,7 @@ class PiWalker(ColorMappedByFuncScene):
|
||||
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)
|
||||
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:
|
||||
# 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
|
||||
@ -858,7 +801,8 @@ class PiWalker(ColorMappedByFuncScene):
|
||||
scale_arrows = self.scale_arrows,
|
||||
number_update_func = number_update_func,
|
||||
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:
|
||||
@ -904,6 +848,9 @@ class PiWalkerRect(PiWalker):
|
||||
"walk_height" : 2,
|
||||
"func" : plane_func_from_complex_func(lambda c: c**2),
|
||||
"double_up" : False,
|
||||
|
||||
# New default for the scenes using this:
|
||||
"display_wind" : True
|
||||
}
|
||||
|
||||
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)]
|
||||
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):
|
||||
CONFIG = {
|
||||
"camera_config" : {"use_z_coordinate_for_display_order": True},
|
||||
@ -938,7 +993,11 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
"initial_upper_y" : 3,
|
||||
"num_iterations" : 1,
|
||||
"num_checkpoints" : 10,
|
||||
|
||||
# Should really merge this into one enum-style variable
|
||||
"display_in_parallel" : False,
|
||||
"display_in_bfs" : False,
|
||||
|
||||
"use_fancy_lines" : True,
|
||||
# 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
|
||||
@ -955,7 +1014,11 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
"show_winding_numbers" : True,
|
||||
|
||||
# Used for UhOhScene;
|
||||
"manual_wind_override" : None
|
||||
"manual_wind_override" : None,
|
||||
|
||||
"show_cursor" : False,
|
||||
|
||||
"linger_parameter" : 0.2,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
@ -976,11 +1039,14 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
obj1.color_using_background_image(bg)
|
||||
|
||||
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
|
||||
linger_rate = squish_rate_func(lambda t : t, 0,
|
||||
fdiv(run_time_base, run_time_with_lingering))
|
||||
|
||||
cursor_base = TextMobject("?")
|
||||
cursor_base.scale(2)
|
||||
|
||||
# Helper functions for manual_wind_override
|
||||
def head(m):
|
||||
if m == None:
|
||||
@ -998,7 +1064,7 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
print "Solver at depth: " + str(cur_depth)
|
||||
|
||||
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):
|
||||
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)
|
||||
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)
|
||||
|
||||
if total_wind == 0:
|
||||
@ -1064,14 +1148,14 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
# their "Nothing here" status?
|
||||
# Or draw a large X or something
|
||||
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:
|
||||
(sub_rect1, sub_rect2) = rect.splits_on_dim(dim_to_split)
|
||||
if dim_to_split == 0:
|
||||
sub_rect_and_sides = [(sub_rect1, 1), (sub_rect2, 3)]
|
||||
else:
|
||||
sub_rect_and_sides = [(sub_rect1, 2), (sub_rect2, 0)]
|
||||
sub_anims = [
|
||||
children = [
|
||||
Animate2dSolver(
|
||||
cur_depth = cur_depth + 1,
|
||||
rect = sub_rect,
|
||||
@ -1084,14 +1168,8 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
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 = DashedLine(*mid_line_points)
|
||||
if self.display_in_parallel:
|
||||
recursive_anim = AnimationGroup(*sub_anims)
|
||||
else:
|
||||
recursive_anim = Succession(*sub_anims)
|
||||
return Succession(anim,
|
||||
ShowCreation(mid_line),
|
||||
recursive_anim
|
||||
)
|
||||
|
||||
return EquationSolver2dNode(Succession(anim, ShowCreation(mid_line)), children)
|
||||
|
||||
lower_x = self.initial_lower_x
|
||||
upper_x = self.initial_upper_x
|
||||
@ -1105,7 +1183,7 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
|
||||
print "Starting to compute anim"
|
||||
|
||||
anim = self.anim = Animate2dSolver(
|
||||
node = Animate2dSolver(
|
||||
cur_depth = 0,
|
||||
rect = rect,
|
||||
dim_to_split = 0,
|
||||
@ -1115,6 +1193,13 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
|
||||
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
|
||||
rect_points = [
|
||||
rect.get_top_left(),
|
||||
@ -1146,6 +1231,11 @@ class EquationSolver2d(ColorMappedObjectsScene):
|
||||
|
||||
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
|
||||
# jarringly popping out and in?
|
||||
#
|
||||
@ -1763,10 +1853,12 @@ class DemonstrateColorMapping(ColorMappedObjectsScene):
|
||||
self.play(ReplacementTransform(background, bright_background))
|
||||
self.wait()
|
||||
|
||||
# Everything in this is manually kept in sync with WindingNumber_G/TransitionFromPathsToBoundaries
|
||||
class LoopSplitScene(ColorMappedObjectsScene):
|
||||
CONFIG = {
|
||||
# TODO: Change this to something more colorful down the midline
|
||||
"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)),
|
||||
"func" : plane_func_by_wind_spec(
|
||||
(-2, 0, 2), (2, 0, 1)
|
||||
),
|
||||
"use_fancy_lines" : True,
|
||||
}
|
||||
|
||||
@ -1776,7 +1868,7 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||
num_bullets = 4,
|
||||
pulse_time = 1,
|
||||
**kwargs):
|
||||
line = Line(start, end, color = WHITE, **kwargs)
|
||||
line = Line(start, end, color = WHITE, stroke_width = 4, **kwargs)
|
||||
if self.use_fancy_lines:
|
||||
line.color_using_background_image(self.background_image_file)
|
||||
anim = LinePulser(
|
||||
@ -1815,35 +1907,24 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||
def pl(a, b):
|
||||
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
|
||||
# Transforms making copies no longer identified with the ContinualAnimation's tracked mobject
|
||||
def fader_bullet(start, end, mob):
|
||||
return UpdateFromAlphaFunc(mob, lambda m, a : m.set_fill(opacity = interpolate(start, end, a)))
|
||||
|
||||
def fader_blist(start, end, blist):
|
||||
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)
|
||||
if x.get_slope() == 0:
|
||||
circle.stretch(0.2, 1)
|
||||
if double_horizontal_stretch:
|
||||
circle.stretch(2, 0)
|
||||
else:
|
||||
circle.stretch(0.2, 0)
|
||||
return circle
|
||||
|
||||
tl_line_trip = pl(tl, tm)
|
||||
left_mid_trip = pl(tm, bm)
|
||||
midline_left_trip = pl(tm, bm)
|
||||
bl_line_trip = pl(bm, bl)
|
||||
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_vmobject = VMobject(*left_square_lines)
|
||||
left_square_bullets = [x[1] for x in left_square_trips]
|
||||
@ -1852,54 +1933,93 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||
tr_line_trip = pl(tm, tr)
|
||||
right_line_trip = pl(tr, br)
|
||||
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_vmobject = VMobject(*right_square_lines)
|
||||
right_square_bullets = [x[1] 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:
|
||||
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.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.play(fader_widthmob(0, 1, right_square_lines_vmobject), *fader_blist(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.play(line_fade(1, faded, left_square_lines_vmobject), *bullet_list_fade(0, 1, right_square_bullets))
|
||||
self.wait()
|
||||
|
||||
return
|
||||
|
||||
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))
|
||||
flash_circles([indicate_circle(l) for l in right_square_lines])
|
||||
self.play(line_fade(faded, 1, left_square_lines_vmobject), *bullet_list_fade(1, 0, right_square_bullets))
|
||||
|
||||
self.wait()
|
||||
self.play(*bullet_list_fade(0, 1, left_square_bullets + right_square_bullets))
|
||||
|
||||
self.play(FadeOut(highlight_circle), FadeOut(mid_lines))
|
||||
# Because FadeOut didn't remove the continual pulsers, we remove them manually
|
||||
self.remove(mid_line_left[1], mid_line_right[1])
|
||||
outside_circlers = [
|
||||
indicate_circle(left_line),
|
||||
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
|
||||
# self.play(
|
||||
# ApplyMethod(left_open_loop.shift, 2 * RIGHT),
|
||||
# ApplyMethod(right_open_loop.shift, 2 * LEFT)
|
||||
# )
|
||||
inner_circle = indicate_circle(midline_lines[0])
|
||||
self.play(FadeIn(inner_circle))
|
||||
self.play(FadeOut(inner_circle), line_fade(1, 0, midline_lines_vmobject), *bullet_list_fade(1, 0, midline_bullets))
|
||||
|
||||
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?
|
||||
class LoopSplitSceneMapped(LoopSplitScene):
|
||||
@ -1928,14 +2048,16 @@ class SolveX5MinusXMinus1(EquationSolver2d):
|
||||
"use_fancy_lines" : True,
|
||||
}
|
||||
|
||||
class SolveX5MinusXMinus1Parallel(EquationSolver2d):
|
||||
class SolveX5MinusXMinus1Parallel(SolveX5MinusXMinus1):
|
||||
CONFIG = {
|
||||
"func" : plane_func_from_complex_func(lambda c : c**5 - c - 1),
|
||||
"num_iterations" : 5,
|
||||
"use_fancy_lines" : True,
|
||||
"display_in_parallel" : True
|
||||
}
|
||||
|
||||
class SolveX5MinusXMinus1BFS(SolveX5MinusXMinus1):
|
||||
CONFIG = {
|
||||
"display_in_bfs" : True
|
||||
}
|
||||
|
||||
class PreviewClip(EquationSolver2d):
|
||||
CONFIG = {
|
||||
"func" : example_plane_func,
|
||||
@ -1944,6 +2066,23 @@ class PreviewClip(EquationSolver2d):
|
||||
"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
|
||||
# Note: May want to do an ordinary square scene, then MappingCamera it into a circle
|
||||
# class BorsukUlamScene(PiWalker):
|
||||
@ -2468,6 +2607,12 @@ class OneFifthTwoFifthWinder(SpecifiedWinder):
|
||||
"step_size" : 0.01,
|
||||
"show_num_plane" : False,
|
||||
"step_run_time" : 6,
|
||||
"num_decimal_points" : 2,
|
||||
}
|
||||
|
||||
class OneFifthOneFifthWinderWithReset(OneFifthTwoFifthWinder):
|
||||
CONFIG = {
|
||||
"wind_reset_indices" : [1]
|
||||
}
|
||||
|
||||
class OneFifthTwoFifthWinderOdometer(OneFifthTwoFifthWinder):
|
||||
@ -2500,6 +2645,8 @@ class CWColorWalk(PiWalkerRect):
|
||||
"walk_width" : 2,
|
||||
"walk_height" : 2,
|
||||
"draw_lines" : False,
|
||||
"display_wind" : False,
|
||||
"step_run_time" : 2
|
||||
}
|
||||
|
||||
class CWColorWalkOdometer(CWColorWalk):
|
||||
@ -2521,7 +2668,8 @@ class CCWColorWalkOdometer(CCWColorWalk):
|
||||
class ThreeTurnWalker(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"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):
|
||||
@ -2561,6 +2709,7 @@ class ZeroTurnWalkerOdometer(ZeroTurnWalker):
|
||||
|
||||
class NegOneTurnWalker(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"step_run_time" : 2,
|
||||
"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):
|
||||
# Ignores dim_to_match and stretch; result will always be a circle
|
||||
# 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.scale_to_fit_width(np.sqrt(mobject.get_width()**2 + mobject.get_height()**2))
|
||||
self.scale(buffer_factor)
|
||||
|
||||
|
Reference in New Issue
Block a user